GPU2.0卡适配指导
更新时间: 2026/02/12
在Gitcode上查看源码

本文档主要介绍如何在openUBMC上适配一张规范2.0的GPU卡。

南向部件驱动适配教程GPU篇

本教程面向从事GPU适配的南向部件驱动开发者,详细阐述了从接口设计到代码实现的完整流程。

目录

1. 概述

List of abbreviations 缩略语清单

Abbreviations 缩略语Full spelling 英文全名Chinese explanation 中文解释
BMCBaseboard Management Controller基板管理控制器
PCIePeripheral Component Interconnect Express高速外设组件互连
GPUGraphics Processing Unit图形处理器
Component Drivers是一个基于C++17的硬件组件驱动程序框架,主要面向服务器板卡组件管理场景。详细介绍可参考本项目的overview.md文档

2. 前置准备

2.1 准备编译环境

可参考社区的Docker开发环境搭建。在完成环境搭建后,推荐通过scripts目录下的smart_build.sh完成首次编译,如果希望通过手动构建的方式,可参考本项目的README.md文档

2.2 了解驱动规范

驱动规范定义了驱动需要实现的接口。GPU的驱动规范可参考本项目归档的GPU驱动规范

2.3 了解工程目录

Component Drivers项目采用分层架构设计,GPU驱动适配需要了解以下关键目录结构:

2.3.1 项目整体结构

text
component_drivers/
├── docs/                      # 文档
├── drivers/                   # 驱动实现目录
│   ├── pcie_gpu_card/         # GPU卡驱动
│   ├── bus/                   # 总线驱动
│   └── chip/                  # 芯片驱动
├── gen/                       # 自动生成的接口定义
├── include/                   # 公共头文件
├── libraries/                 # 协议库
│   └── smbus/                 # SMBus协议
└── tests/                     # 测试代码

2.3.2 GPU驱动目录结构

GPU驱动位于 drivers/pcie_gpu_card/ 目录下,按厂商和型号组织:

text
pcie_gpu_card/
├── innosilicon/                    # 厂商目录(如:芯动科技)
│   ├── awm_m11p/                   # 具体GPU型号目录
│   │   ├── awm_m11p_abi.cpp        # ABI导出接口
│   │   ├── awm_m11p_card.cpp       # GPU卡主对象实现
│   │   ├── awm_m11p_card.h         # GPU卡主对象头文件
│   │   ├── awm_m11p_gpu.cpp        # GPU处理器对象实现
│   │   ├── awm_m11p_gpu.h          # GPU处理器对象头文件
│   │   ├── awm_m11p_memory.cpp     # GPU内存对象实现
│   │   ├── awm_m11p_memory.h       # GPU内存对象头文件
│   │   └── meson.build             # 构建配置
│   ├── csr/                        # 设备配置目录
│   │   ├── [VID_DID_SVID_SSID].dds # 设备描述文件
│   │   └── [VID_DID_SVID_SSID].sr  # 部件自描述记录
│   ├── interface/                  # 接口实现目录
│   │   ├── gpu/
│   │   |   ├── power.h/.cpp        # GPU功耗接口实现
│   │   |   └── status.h/.cpp       # GPU状态接口实现
│   │   ├── gpu.h/.cpp              # GPU接口实现
│   │   ├── memory.h/.cpp           # 内存接口实现
│   │   ├── pcie_card.h/.cpp        # PCIe卡接口实现
│   │   ├── pcie_device.h/.cpp      # PCIe设备接口实现
│   │   └── processor.h/.cpp        # 处理器接口实现
│   └── meson.build                 # 构建配置
└── meson.build                     # 构建配置

2.3.3 关键目录说明

目录类型路径说明作用
接口定义gen/include/device_tree/interface/自动生成的接口基类定义定义GPU、Memory、PCIeDevice等接口的属性和方法签名
厂商实现drivers/pcie_gpu_card/[厂商]/[型号]/厂商具体驱动实现继承gen接口,实现GPU管理的实际功能逻辑
协议库libraries/通信协议实现SMBus等协议封装,用于与GPU通信
配置文件drivers/pcie_gpu_card/[厂商]/csr/设备配置文件DDS文件定义设备接口,SR文件定义硬件拓扑

目录关系如下:

2.3.4 GPU设备对象层次

GPU驱动中涉及的主要设备对象:

对象类型说明示例类
GPU卡主对象代表整个GPU卡awm_m11p_card
GPU处理器对象代表GPU芯片本身awm_m11p_gpu
GPU内存对象代表GPU显存awm_m11p_memory

这些对象通过继承不同的gen接口来实现相应的功能,如PCIeDevice、Gpu、Memory等接口

2.4 了解GPU支持的协议类型

不同厂商的GPU支持的带外管理协议不同,这需要开发者去向厂家了解对应的协议。一般来讲,GPU的带外管理协议是在SMBus协议基础之上实现的,例如NVIDIA GPU的SMBPBI协议。通过了解GPU的带外管理协议,就可以清楚接下来可以实现哪些接口并且如何实现它们

3. 适配流程

以适配芯动科技的风华1号为例

3.1 创建目录

drivers/pcie_gpu_card/ 下创建如下目录与文件:

text
pcie_gpu_card/
├── innosilicon/                    # 厂商目录(如:芯动科技)
│   ├── awm_m11p/                   # 具体GPU型号目录
│   │   ├── awm_m11p_abi.cpp        # ABI导出接口
│   │   ├── awm_m11p_card.cpp       # GPU卡主对象实现
│   │   ├── awm_m11p_card.h         # GPU卡主对象头文件
│   │   ├── awm_m11p_gpu.cpp        # GPU处理器对象实现
│   │   ├── awm_m11p_gpu.h          # GPU处理器对象头文件
│   │   ├── awm_m11p_memory.cpp     # GPU内存对象实现
│   │   ├── awm_m11p_memory.h       # GPU内存对象头文件
│   │   └── meson.build             # 构建配置
│   ├── csr/                        # 设备配置目录
│   │   ├── [VID_DID_SVID_SSID].dds # 设备描述文件
│   │   └── [VID_DID_SVID_SSID].sr  # 部件自描述记录
│   ├── interface/                  # 接口实现目录
│   │   ├── gpu/
│   │   |   ├── power.h/.cpp        # GPU功耗接口实现
│   │   |   └── status.h/.cpp       # GPU状态接口实现
│   │   ├── gpu.h/.cpp              # GPU接口实现
│   │   ├── memory.h/.cpp           # 内存接口实现
│   │   ├── pcie_card.h/.cpp        # PCIe卡接口实现
│   │   ├── pcie_device.h/.cpp      # PCIe设备接口实现
│   │   └── processor.h/.cpp        # 处理器接口实现
│   └── meson.build                 # 构建配置
└── meson.build                     # 构建配置

说明:

  • awm_m11p_card 为GPU卡主对象,组合并管理GPU处理器与内存等子对象;
  • awm_m11p_gpu 为GPU处理器对象,承载 GpuGpu.StatusGpu.Power 等接口实现;
  • awm_m11p_memory 为显存对象,实现 Memory 相关接口;
  • interface/ 下的文件继承 gen/include/device_tree/interface/ 中对应基类,负责协议交互与属性更新;
  • csr/ 目录中的 .dds.sr 文件分别定义对象/接口与硬件管理拓扑;
  • meson.build 用于声明源文件、依赖与安装目标(最终生成 lib<model>.so)。

3.2 配置设备描述文件

文件命名规则

text
格式:14140130_[VID][DID]_[SVID][SSID].dds
示例:14140130_19e50222_19e50052.dds
说明:VID=0x19e5, DID=0x0222, SVID=0x19e5, SSID=0x0052

DDS文件内容

json
{
    "Schema": "dds-v1",
    "Type": "Component",
    "DeviceCategory": "PCIeGpuCard",
    "ID": "1fe01010_1fe01010",
    "Objects": {
        "PCIeGpuCard": {
            "Path": "/bmc/dev/Systems/:SystemId/PCIeGpuCard/:Id",
            "Interfaces": [
                "bmc.dev.PCIeDevice",
                "bmc.dev.PCIeCard"
            ]
        },
        "GraphicsController": {
            "Path": ":Parent/GraphicsController/:Id",
            "Interfaces": [
                "bmc.dev.Processor",
                "bmc.dev.Gpu",
                "bmc.dev.Gpu.Power",
                "bmc.dev.Gpu.Status"
            ]
        },
        "Memory": {
            "Path": ":Parent/Memory/:Id",
            "Interfaces": [
                "bmc.dev.Memory"
            ]
        }
    }
}

关键字段说明

字段说明注意事项
DeviceCategory设备类别必须是"PCIeGpuCard"
Objects对象定义键名必须与ABI中的device_name一致
Path对象路径:SystemId:Id是动态参数,:Parent表示父对象
Interfaces接口列表必须与自动生成接口头文件中MC_OBJECT宏中声明的接口一致

配置要点

  1. 接口列表要完整Interfaces必须包含代码中MC_OBJECT声明的所有接口
  2. 对象名称要匹配Objects的键名(如PCIeGpuCard)必须与ABI注册时的device_name一致
  3. 路径格式固定:主对象路径和子对象路径格式不要修改

3.3 配置部件自描述记录

SR文件定义硬件拓扑、芯片配置和对象初始值,用于复杂的硬件管理场景。

文件命名规则

text
格式:14140130_[VID][DID]_[SVID][SSID].sr
示例:14140130_19e50222_19e50052.sr
说明:文件名必须与DDS文件一致

SR文件结构

json
{
    "FormatVersion": "5.00",
    "DataVersion": "5.00",
    "Unit": {
        "Type": "PCIeGpuCard",
        "Name": "PCIeGpuCard_1",
        "Compatible": ["awm_m11p"]
    },
    "ManagementTopology": {
        // 硬件拓扑配置(I2C总线、芯片连接关系)
    },
    "Objects": {
        // 对象初始配置和属性值
    }
}

Unit配置(部件单元)

字段说明注意事项
Type部件类别标识必须是"PCIeGpuCard"
Name部件主管理对象名称一般是"PCIeGpuCard_1",必须与"Objects"下的一致,在同一文件中必须唯一
Compatible兼容性标识数组必须与pcie_gpu_card/innosilicon/awm_m11p/meson.build中指定的驱动名称一致,一般使用型号名称,支持多个兼容的设备型号

ManagementTopology配置(I2C拓扑)

一般GPU卡通过SMBus提供带外能力

json
"ManagementTopology": {
    "Anchor": {
        "Buses": [
            "I2cMux_Chan"
        ]
    },
    "I2cMux_Chan": {
        "Chips": [
            "Chip_M11P"      // GPU提供带外管理能力的芯片 
        ]
    }
},
"Objects": {
    "Chip_M11P": {
        "Address": 158,     // I2C地址:0x9E
        "AddrWidth": 1,
        "OffsetWidth": 1,
        "WriteTmout": 100,
        "ReadTmout": 100,
        "HealthStatus": 0
    }
}

GPU卡对象配置(PCIeGpuCard_1)

定义GPU卡的初始属性值:

bmc.dev.PCIeDevice
属性说明建议实现方式
DeviceNamePCIe设备名称通常会包含槽位号和型号
FunctionClass功能分类GPU卡固定为3
VendorId制造商Id按需配置
DeviceId设备Id按需配置
SubSystemVendorId子厂商Id按需配置
SubSystemDeviceId子设备Id按需配置
Slot槽位号固定配"${Slot}"
DeviceType设备类型取值参考Redfish标准规范中PCIeDevice资源的同名属性
SlotTypePCIe设备所在槽位的类型取值参考Redfish标准规范中PCIeDevice资源的同名属性
FunctionProtocolPCIe功能协议取值参考Redfish标准规范中PCIeDevice资源的同名属性,对于GPU卡,固定配"PCIe"
FunctionTypePCIe功能类型取值参考Redfish标准规范中PCIeDevice资源的同名属性,对于GPU卡,固定配"Physical"
ComponentTypePCIe设备部件类型PCIe卡固定为8
Container容纳此PCIe设备的Component对象名称,由上级连接器传入固定配"${Container}"
GroupPosition资源协作路径信息,由上级连接器传入、拼接而成,可作为对象唯一标识固定配"PCIeDevice_1_${GroupPosition}"
bmc.dev.PCIeCard
属性说明建议实现方式
Name产品名称按需配置
Model产品型号按需配置
Manufacturer厂商按需配置
PartNumber部件编码按需配置
Description描述按需配置
BoardName单板名称按需配置

GPU对象配置(GraphicsController_1)

bmc.dev.Processor
属性说明建议实现方式
SystemId系统ID一般配置1
Id处理器ID一般配置1
Presence在位状态一般配置1
Manufacturer厂商按需配置
Model型号按需配置
Location位置信息引用父对象PCIeGpuCard_1.Location
bmc.dev.Gpu
属性说明建议实现方式
TemperatureCelsiusGPU温度一般需要通过带外管理协议获取,本例通过配置Scanner实现
bmc.dev.Gpu.Power
属性说明建议实现方式
PowerWatts功耗配置默认值0
PowerBrakeState功耗限制状态配置默认值255
ExternalPowerSufficient外部供电是否充足配置默认值255
bmc.dev.Gpu.Status
属性说明建议实现方式
ECCModeEnabledECC模式当时使能状态配置默认值255
ECCModePendingEnabledECC模式重启后使能状态配置默认值255
ResetRequired是否需要重置配置默认值255
DoubleBitErrorPageCount双bit失效故障页计数配置默认值0
SingleBitErrorPageCount单bit失效故障页计数配置默认值0

内存对象配置(Memory_1)

bmc.dev.Memory
属性说明建议实现方式
DimmNum内存Id一般配置1
Manufacturer厂商按需配置

SR文件配置要点

  1. 父子关系:使用@Parent字段指定对象的层次关系
  2. 动态参数${Slot}等参数会在运行时替换
  3. 初始值:255通常表示"无效值"或"未初始化"
  4. 接口完整性:SR中定义的接口必须与DDS文件中声明的接口一致

3.4 创建GPU卡主对象

参考 awm_m11p_card.h

cpp
#ifndef __AWM_M11P_CARD_H__
#define __AWM_M11P_CARD_H__

#include "interface/pcie_card.h"
#include "interface/pcie_device.h"
#include <mc/engine.h>

namespace dev {

class awm_m11p_card : public mc::engine::object<awm_m11p_card> {
public:
    // MC_OBJECT宏定义:类名、对象类型、路径模式、接口列表
    MC_OBJECT(awm_m11p_card, "PCIeGpuCard",
              "/bmc/dev/Systems/1/PCIeGpuCard/${object_name}", 
              (PCIeDevice)(PCIeCard))
    awm_m11p_card()  = default;    // 构造函数
    ~awm_m11p_card() = default;    // 析构函数

    // 生命周期方法
    bool start();
    bool stop();
    bool init(mc::mutable_dict& csr_object, const mc::dict& connector);
    bool replace_dynamic_property(mc::mutable_dict& object, const mc::dict& connector);

    // 接口成员变量
    PCIeDevice m_pcie_device;
    PCIeCard   m_pcie_card;
    uint8_t    SystemId;
};

} // namespace dev

#endif // __AWM_M11P_CARD_H__

关键说明

1. 头文件包含:

"interface/pcie_card.h"包含<device_tree/interface/PCIeCard.h>,该路径实际指向gen/include/device_tree/interface/下的同名文件。这是因为在根目录下的meson.build有一句include_directories('gen/include'),,gen/include目录被添加到了编译系统的include路径中。其他的头文件包含同理

2. MC_OBJECT宏的参数:

  • 第1个:类名
  • 第2个:设备类型(固定为"PCIeGpuCard"
  • 第3个:对象路径模板(固定格式)
  • 第4个:实现的接口列表(按需选择)

3. 接口选择原则:

接口必需性说明
PCIeDevicePCIeCard必需基础接口

3.5 实现GPU卡主对象

参考 awm_m11p_card.cpp

cpp
#include "awm_m11p_card.h"
#include <mc/core/timer.h>
#include <mc/log.h>
#include <thread>
#include <vector>

namespace dev {

// 启动方法:启动GPU卡任务,本示例不涉及
bool awm_m11p_card::start() {
    return true;
}

// 停止方法:停止GPU卡任务并清理资源,本示例不涉及
bool awm_m11p_card::stop() {
    return true;
}

// 在运行时替换SR配置文件中的动态占位符为实际值,bmc.dev.PCIeDevice接口下存在需要替换的动态占位符
bool awm_m11p_card::replace_dynamic_property(mc::mutable_dict& object, const mc::dict& connector) {
    m_pcie_device.replace_dynamic_property(object, connector);
    return true;
}

// 初始化方法,调用replace_dynamic_property,确保对象初始化时使用的是替换后的实际值
bool awm_m11p_card::init(mc::mutable_dict& csr_object, const mc::dict& connector) {
    try {
        replace_dynamic_property(csr_object, connector);
        from_variant(csr_object, *this);
    } catch (const std::exception& e) {
        elog("awm_m11p_card init failed, exception: ${exception}", ("exception", e.what()));
        return false;
    }
    return true;
}

} // namespace dev

MC_REFLECT(dev::awm_m11p_card,
           ((SystemId, "SystemId"))((m_pcie_device, "bmc.dev.PCIeDevice"))((m_pcie_card,
                                                                            "bmc.dev.PCIeCard")))

关键说明

方法职责
init()加载配置文件中的属性值,使用from_variant自动映射
start()启动方法,如果有轮询任务,应当在本函数启动
stop()停止方法,如果有轮询任务,应当在本函数停止
replace_dynamic_property()在运行时替换SR配置文件中的动态占位符为实际值

3.5 实现GPU对象

参考 awm_m11p_gpu.cpp

cpp
#include "awm_m11p_gpu.h"
#include "mc/log.h"

namespace dev {

// 启动方法:启动GPU任务,本示例不涉及
bool awm_m11p_gpu::start() {
    return true;
}

// 停止方法:停止GPU任务并清理资源,本示例不涉及
bool awm_m11p_gpu::stop() {
    return true;
}

// 初始化方法
bool awm_m11p_gpu::init(mc::mutable_dict& config, const mc::dict& connector) {
    try {
        from_variant(config, *this);
    } catch (const std::exception& e) {
        elog("awm_m11p_gpu init failed, exception: ${exception}", ("exception", e.what()));
        return false;
    }
    return true;
}

// 恢复默认属性
bool awm_m11p_gpu::reset_default_properties() {
    m_processor.Presence                = false;
    m_gpu.InfoRomVersion                = "";
    m_gpu.BuildDate                     = "";
    m_gpu.UUID                          = "";
    m_gpu.Utilization                   = 0;
    m_gpu.TemperatureCelsius            = 0;
    m_gpu_power.PowerWatts              = 0;
    m_gpu_power.PowerBrakeState         = 255;
    m_gpu_power.ExternalPowerSufficient = 255;
    m_gpu_status.ECCModeEnabled         = 255;
    m_gpu_status.ECCModePendingEnabled  = 255;
    m_gpu_status.ResetRequired          = 255;

    return true;
}

} // namespace dev

MC_REFLECT(dev::awm_m11p_gpu,
           ((m_processor, "bmc.dev.Processor"))((m_gpu, "bmc.dev.Gpu"))(
               (m_gpu_power, "bmc.dev.Gpu.Power"))((m_gpu_status, "bmc.dev.Gpu.Status")))

关键说明

方法职责
reset_default_properties()恢复默认属性

3.6 实现接口类

  • interface目录按接口定义层级分布
  • 接口类继承自gen命名空间的基类,负责具体的协议实现和数据更新
  • 由于本示例中的GPU并未涉及复杂协议,因此接口类并不需要做什么修改
  • 通用的接口,如bmc.dev.PCIeDevice,对于所有PCIe设备都会涉及,因此需要考虑放到公共目录,避免重复代码
  • 轮询任务通常在接口类实现,以下是网卡的一个案例
cpp
void NetworkAdapter::start_ncsi_update_task_huawei(ncsi_over_mctp_hw_ptr ncsi_over_mctp,
                                                   mc::milliseconds      interval) {
    // 创建定时器实例
    m_ncsi_timer            = new mc::core::timer(this);
    // 记录协议实体地址,当驱动对象实现层在调用接口类时完成赋值
    m_ncsi_over_mctp_huawei = ncsi_over_mctp;
    // 具体的轮询动作
    m_ncsi_timer->timeout.connect([this]() {
        update_firmware_version_by_ncsi();
    });
    m_ncsi_timer->set_single_shot(false);
    // 启动轮询
    m_ncsi_timer->start(interval);
}

void NetworkAdapter::stop_ncsi_update_task() {
    if (m_ncsi_timer) {
        // 停止轮询
        m_ncsi_timer->stop();
        // 删除定时器实例
        delete m_ncsi_timer;
        m_ncsi_timer = nullptr;
    }
    if (m_ncsi_over_mctp_huawei) {
        m_ncsi_over_mctp_huawei = nullptr;
    }
}

3.7 实现ABI导出

ABI(Application Binary Interface)是驱动程序与框架交互的标准接口

参考 awm_m11p_abi.cpp

cpp

#include "awm_m11p_card.h"
#include "awm_m11p_gpu.h"
#include "awm_m11p_memory.h"
#include "devmon/driver_abi.h"
#include "mc/log.h"
#include <iostream>
#include <memory>
#ifndef DEVICE_DRIVER_NAME
#define DEVICE_DRIVER_NAME "awm_m11p"
#endif

using namespace dev;

extern "C" {

// ============================================
// GPU卡对象ABI函数
// ============================================

// 创建GPU卡对象:分配内存并设置基本属性
driver_handle_t create_awm_m11p_card(void* service, const char* name) {
    // 参数检查
    // 创建awm_m11p_card对象
    // 设置service和对象名称
    // 返回对象指针
}

// 初始化GPU卡对象:加载配置
status_t init_awm_m11p_card(driver_handle_t device, void* csr_object, void* connector) {
    // 参数检查
    // 类型转换
    // 调用对象的init方法
    // 返回状态码
}

// 启动GPU卡对象:启动设备和协议
status_t start_awm_m11p_card(driver_handle_t device) {
    // 参数检查
    // 调用对象的start方法
    // 返回状态码
}

// 停止GPU卡对象:停止设备和清理资源
status_t stop_awm_m11p_card(driver_handle_t device) {
    // 参数检查
    // 调用对象的stop方法
    // 返回状态码
}

// ============================================
// GPU对象ABI函数
// ============================================

// 创建GPU对象:分配内存并设置基本属性
driver_handle_t create_awm_m11p_gpu(void* service, const char* name) {
    // 参数检查
    // 创建awm_m11p_card对象
    // 设置service和对象名称
    // 返回对象指针
}

// 初始化GPU对象:加载配置
status_t init_awm_m11p_gpu(driver_handle_t device, void* csr_object, void* connector) {
    // 参数检查
    // 类型转换
    // 调用对象的init方法
    // 返回状态码
}

// 启动GPU对象:启动设备和协议
status_t start_awm_m11p_gpu(driver_handle_t device) {
    // 参数检查
    // 调用对象的start方法
    // 返回状态码
}

// 停止GPU对象:停止设备和清理资源
status_t stop_awm_m11p_gpu(driver_handle_t device) {
    // 参数检查
    // 调用对象的stop方法
    // 返回状态码
}

// ============================================
// 内存对象ABI函数
// ============================================

// 创建内存对象:分配内存并设置基本属性
driver_handle_t create_awm_m11p_memory(void* service, const char* name) {
    // 参数检查
    // 创建awm_m11p_card对象
    // 设置service和对象名称
    // 返回对象指针
}

// 初始化内存对象:加载配置
status_t init_awm_m11p_memory(driver_handle_t device, void* csr_object, void* connector) {
    // 参数检查
    // 类型转换
    // 调用对象的init方法
    // 返回状态码
}

// 启动内存对象:启动设备和协议
status_t start_awm_m11p_memory(driver_handle_t device) {
    // 参数检查
    // 调用对象的start方法
    // 返回状态码
}

// 停止内存对象:停止设备和清理资源
status_t stop_awm_m11p_memory(driver_handle_t device) {
    // 参数检查
    // 调用对象的stop方法
    // 返回状态码
}

// ============================================
// 驱动注册
// ============================================

device_driver_t awm_m11p_card_device_driver = {.device_name = "PCIeGpuCard",  // 设备类型名(必须与DDS文件一致)
                                               .ctor        = create_awm_m11p_card,
                                               .init        = init_awm_m11p_card,
                                               .start       = start_awm_m11p_card,
                                               .stop        = stop_awm_m11p_card};

device_driver_t awm_m11p_gpu_device_driver = {.device_name = "GraphicsController",   // 设备类型名(必须与DDS文件一致)
                                              .ctor        = create_awm_m11p_gpu,
                                              .init        = init_awm_m11p_gpu,
                                              .start       = start_awm_m11p_gpu,
                                              .stop        = stop_awm_m11p_gpu};

device_driver_t awm_m11p_memory_device_driver = {.device_name = "Memory",   // 设备类型名(必须与DDS文件一致)
                                                 .ctor        = create_awm_m11p_memory,
                                                 .init        = init_awm_m11p_memory,
                                                 .start       = start_awm_m11p_memory,
                                                 .stop        = stop_awm_m11p_memory};

// 驱动数组:包含所有设备类型的驱动
device_driver_t awm_m11p_driver[] = {awm_m11p_card_device_driver, awm_m11p_gpu_device_driver,
                                     awm_m11p_memory_device_driver};

// 驱动注册函数:框架调用此函数获取驱动列表
status_t        register_device_driver(device_driver_t** device_driver, uint8_t* count) {
    *device_driver = awm_m11p_driver;
    *count         = sizeof(awm_m11p_driver) / sizeof(awm_m11p_driver[0]);
    return STATUS_OK;
}
}
  • 所有ABI函数必须用extern "C"包装,确保C语言兼容
  • 每个设备类型需要4个函数:createinitstartstop
  • device_name必须与DDS文件中的对象类型匹配
  • register_device_driver是框架调用的入口函数

3.8 配置构建系统

参考 meson.build

meson
# 源文件列表
awm_m11p_sources = files(
  'awm_m11p_abi.cpp',
  'awm_m11p_card.cpp',
  'awm_m11p_gpu.cpp',
  'awm_m11p_memory.cpp',
) + interface_sources

# 动态库(用于部署)
libawm_m11p = shared_library(
  'awm_m11p',
  awm_m11p_sources,
  include_directories: include_dirs + innosilicon_interface_inc,
  dependencies: [dev_deps],
  name_prefix: 'lib',
  name_suffix: 'so',
  install: true,
  install_dir: drivers_install_dir,
)

# 指定部署路径
if build_tests and meson_build
  custom_target(
    'copy_libawm_m11p',
    output: 'copy_libawm_m11p',
    command: ['cp', libawm_m11p.full_path(), drivers_install_dir],
    depends: libawm_m11p,
    build_by_default: true,
  )
endif

关键说明

  • 库名会成为驱动SO文件名:libawm_m11p.so,注意和CSR配置一致
  • 依赖关系要明确列出,避免链接错误

3.9 编写开发者测试

单元测试

  • tests/drivers路径下新增pcie_gpu_card/innosilicon/awm_m11p目录
  • 在此目录下新增test_awm_m11p_abi.cpp
  • 代码框架可以参考test_hi182x_abi.cpp
cpp
class hi182x_test_service : public mc::engine::service {
public:
    hi182x_test_service(const std::string& name) : mc::engine::service(name) {
    }
};

// 该类继承自TestWithEngine,用于测试Hi182x网卡驱动的ABI接口
class Hi182xAbiTest : public mc::test::TestWithEngine {
public:
    // 测试套件初始化(所有测试用例执行前调用一次)
    static void SetUpTestSuite() {
        // 调用基类的SetUpTestSuite初始化测试引擎
        TestWithEngine::SetUpTestSuite();
        // 初始化并启动测试服务
        m_test_service.init();
        m_test_service.start();
        // 动态加载Hi182x驱动库
        load_driver();
    }
    
    // 测试套件清理(所有测试用例执行后调用一次)
    static void TearDownTestSuite() {
        // 停止测试服务
        m_test_service.stop();
        // 关闭动态库句柄,释放资源
        if (m_driver_handle != nullptr) {
            dlclose(m_driver_handle);
            m_driver_handle = nullptr;
        }
        // 清理测试引擎资源
        TestWithEngine::TearDownTestSuite();
    }

    // 单个测试用例初始化(每个测试用例执行前调用)
    void SetUp() override {
    }

    // 单个测试用例清理(每个测试用例执行后调用)
    void TearDown() override {
    }

    // 动态加载驱动库并注册设备驱动
    static void load_driver() {
        // 使用dlopen动态加载驱动库,RTLD_LAZY表示延迟绑定符号
        m_driver_handle = dlopen("./opt/bmc/drivers/libhisi_182x.so", RTLD_LAZY);
        if (m_driver_handle == nullptr) {
            MC_THROW(mc::system_exception, "Failed to load driver: ${error}", ("error", dlerror()));
        }
        // 从动态库中查找并获取register_device_driver函数
        register_device_driver_func func = reinterpret_cast<register_device_driver_func>(
            dlsym(m_driver_handle, "register_device_driver"));
        MC_ASSERT_THROW(func, mc::bad_function_call_exception,
                        "Failed to get export_device_driver function: ${error}",
                        ("error", dlerror()));
        // 调用驱动注册函数,获取设备驱动列表
        device_driver_t* devices       = nullptr;
        uint8_t          devices_count = 0;
        status_t         ret           = func(&devices, &devices_count);
        MC_ASSERT_THROW(ret == STATUS_OK, mc::bad_function_call_exception,
                        "Failed to get device driver manager: ${error}", ("error", ret));

         // 清空并重新填充设备驱动映射表
        m_devices.clear();
        // 预分配空间,提高性能
        m_devices.reserve(devices_count);
        // 遍历所有设备驱动类型,存储到映射表中(key为device_name,value为device_driver_t)
        for (uint8_t i = 0; i < devices_count; i++) {
            auto device_driver = std::make_shared<device_driver_t>(devices[i]);
            m_devices.emplace(device_driver->device_name, device_driver);
        }
    }
    // 连接器(connector)用于传递上级设备的信息给子设备
    mc::dict create_test_connector(uint8_t system_id, uint8_t position, uint8_t slot) {
        return mc::dict{{"SystemId", system_id}, {"Position", position}, {"Slot", slot}};
    }
    // 测试服务实例,提供引擎服务环境
    static hi182x_test_service                                               m_test_service;
    // 设备驱动映射表,key为设备类型名(如"PCIeNicCard"),value为设备驱动信息
    static std::unordered_map<std::string, std::shared_ptr<device_driver_t>> m_devices;
    // 动态库句柄,用于dlopen/dlclose操作
    static void*                                                             m_driver_handle;
};
// ========== 静态成员变量定义 ==========

// 初始化测试服务实例,服务名称为"bmc.kepler.test_hi182x"
// 该服务为测试提供引擎服务环境,支持设备对象的创建和管理
hi182x_test_service Hi182xAbiTest::m_test_service{"bmc.kepler.test_hi182x"};

// 初始化设备驱动映射表(默认为空)
// 在SetUpTestSuite中通过load_driver()填充,存储所有注册的设备驱动类型
// key: 设备类型名称(如"PCIeNicCard"、"NicPort"、"OpticalTransceiver")
// value: 设备驱动信息(包含ctor、init、start、stop等函数指针)
std::unordered_map<std::string, std::shared_ptr<device_driver_t>> Hi182xAbiTest::m_devices;

// 初始化动态库句柄为nullptr
// 在SetUpTestSuite中通过dlopen加载驱动库后设置
// 在TearDownTestSuite中通过dlclose释放
void* Hi182xAbiTest::m_driver_handle = nullptr;
  • 代码框架编写完后,接下来按需编写测试用例,建议覆盖init/create/start/stop各阶段,并覆盖相应的异常场景

结语

GPU驱动适配当前并未涉及到复杂协议,未来在适配各家厂商GPU的时候可能仍会遇到各种各样的问题,欢迎驱动开发者们积极在社区沟通交流,共同建设南向设备驱动规范。