Arm CMN700 RAS固件优先错误注入实现详解
1. CMN700 RAS固件优先软件实现概述
在Arm Neoverse平台中,CMN-700(Coherent Mesh Network)作为关键互连组件,其可靠性、可用性和可维护性(RAS)特性对系统稳定性至关重要。本文将详细介绍一种基于固件优先(Firmware First)架构的CMN700 RAS错误注入实现方案,该方案已在Neoverse N2/V2等平台上验证通过。
固件优先架构的核心优势在于:
- 错误处理路径最短化,确保关键错误能被及时捕获
- 隔离操作系统与硬件错误处理细节,提升系统鲁棒性
- 支持统一错误报告机制,便于平台级错误管理
典型错误注入流程包含以下阶段:
- 用户空间软件通过Linux内核接口发起错误注入请求
- 内核通过ACPI EINJ表与SCP固件通信
- SCP固件在CMN700中触发错误注入
- CMN700生成错误中断并由Trusted Firmware-A处理
- 错误信息通过SCMI协议通知Linux内核
2. SCP固件实现细节
2.1 CMN700错误注入模块创建
在SCP固件中创建独立错误注入模块是推荐做法,既能保持功能隔离,又便于按需启用/禁用。模块结构如下:
module/cmn700_einj/ ├── include/ │ └── mod_cmn700_einj.h ├── src/ │ └── mod_cmn700_einj.c └── Makefile关键初始化函数实现要点:
static int mod_cmn700_einj_init(fwk_id_t module_id, unsigned int element_count, const void *config) { /* 验证配置参数有效性 */ if (config == NULL) return FWK_E_PARAM; /* 初始化模块全局状态 */ module_ctx.config = (const struct mod_cmn700_einj_module_config *)config; module_ctx.state = MOD_CMN700_EINJ_STATE_INITIALIZED; return FWK_SUCCESS; }注意事项:模块初始化阶段应避免执行硬件操作,仅完成软件状态初始化。硬件相关配置应在bind阶段完成后进行。
2.2 SCMI SMT邮箱配置
共享内存传输(SMT)是实现固件与操作系统通信的关键机制。配置要点包括:
- 内存区域划分(scp_software_mmap.h):
#define SCP_EINJ_PAYLOAD_A2P_BASE (SCP_SDS_NONSECURE_BASE + SCP_SDS_NONSECURE_SIZE) #define SCP_EINJ_PAYLOAD_SIZE (128) /* 足够容纳错误注入参数 */- 邮箱服务注册(config_smt.c):
[SCP_PLATFORM_SCMI_SERVICE_IDX_EINJ] = { .name = "EINJ", .data = &((struct mod_smt_channel_config) { .type = MOD_SMT_CHANNEL_TYPE_COMPLETER, .policies = MOD_SMT_POLICY_INIT_MAILBOX, .mailbox_address = (uintptr_t)SCP_EINJ_PAYLOAD_A2P_BASE, .mailbox_size = SCP_EINJ_PAYLOAD_SIZE, .driver_id = FWK_ID_SUB_ELEMENT_INIT(FWK_MODULE_IDX_MHU2, SCP_PLATFORM_MHU_DEVICE_IDX_SCP_AP_S_CLUS0, 1), .signal_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_CMN700, MOD_CMN700_EINJ_API_IDX_TRANSPORT), }) }关键数据结构定义:
struct mod_payload_memory { uint32_t reserved0; uint32_t status; /* 操作状态码 */ uint64_t reserved1; uint32_t flags; /* 错误注入标志位 */ uint32_t length; /* 消息头+负载总长度 */ uint32_t message_header; /* SCMI消息头 */ uint32_t payload[]; /* 实际错误参数 */ };3. 平台ACPI配置
3.1 EDK2中EINJ表启用
在平台描述文件(如RdN2.dsc)中启用EINJ支持:
[PcdsFeatureFlag.common] gArmSgiTokenSpaceGuid.PcdEinjSupported|TRUE配置邮箱地址(平台内存映射需保持一致):
[PcdsFixedAtBuild.common] gArmSgiTokenSpaceGuid.PcdEinjPayloadBase|0x0600051C3.2 ACPI注入指令配置
在PlatformDxe中配置EINJ触发指令:
{ EFI_ACPI_6_3_EINJ_TRIGGER_ERROR, EFI_ACPI_6_3_EINJ_WRITE_REGISTER_VALUE, 0, // Flags 0, // Reserved { EFI_ACPI_6_3_SYSTEM_MEMORY, 32, 0, EFI_ACPI_6_3_DWORD, PcdGet64(PcdEinjPayloadBase) }, 0x0000000000000000, // 注入目标标识 0xffffffffffffffff // 位掩码 }实操技巧:ACPI表配置后需使用acpidump工具验证布局是否正确,确保操作系统能正确解析EINJ表。
4. CMN700寄存器扩展
4.1 错误控制寄存器映射
在CMN700模块中扩展HN-F(Home Node Fully-coherent)寄存器定义:
struct cmn700_hnf_reg { /* ... 其他寄存器定义 ... */ FWK_RW uint64_t ERR_CTLR; /* 0x3008 错误控制 */ uint8_t RESERVED7[0x3030 - 0x3010]; FWK_RW uint64_t ERR_INJ; /* 0x3030 错误注入 */ };错误注入使能位定义:
#define ED_ENABLE (1 << 0) /* 使能可纠正错误检测 */ #define DE_ENABLE (1 << 1) /* 使能不可纠正错误检测 */ #define UI_ENABLE (1 << 2) /* 使能未初始化访问错误 */ #define FI_ENABLE (1 << 3) /* 使能强制错误注入 */ #define CFI_ENABLE (1 << 8) /* 使能持续错误注入 */4.2 错误注入API实现
核心错误注入函数实现逻辑:
static int cmn700_einj_inject_error(uint32_t cmn_node_id, uint32_t err_type) { struct cmn700_hnf_reg* hnf_reg = find_hnf_by_node_id(cmn_node_id); if (!hnf_reg) { FWK_LOG_ERR("[CMN700 EINJ] Invalid node ID: %u", cmn_node_id); return FWK_E_PARAM; } /* 配置错误类型 */ uint64_t err_ctlr_val = ED_ENABLE | DE_ENABLE; if (err_type & ERR_TYPE_FATAL) err_ctlr_val |= FI_ENABLE; hnf_reg->ERR_CTLR = err_ctlr_val; /* 设置错误源并触发注入 */ hnf_reg->ERR_INJ = (cpu_node_id << HNF_ERR_INJ_SRC_ID_POS) | HNF_ERR_INJ_EN; FWK_LOG_INFO("[CMN700 EINJ] Injected error to node %u", cmn_node_id); return FWK_SUCCESS; }避坑指南:错误注入后必须及时清除ERR_INJ寄存器,否则会导致持续错误注入干扰系统运行。
5. Trusted Firmware错误处理
5.1 中断路由配置
在平台初始化阶段配置CMN700 RAS中断路由:
void sgi_ras_intr_configure(int intr, int intr_type) { plat_ic_set_interrupt_type(intr, INTR_TYPE_EL3); plat_ic_set_interrupt_priority(intr, PLAT_RAS_PRI); plat_ic_clear_interrupt_pending(intr); if (intr_type == SGI_RAS_INTR_TYPE_SPI) { plat_ic_set_spi_routing(intr, INTR_ROUTING_MODE_ANY, (u_register_t)read_mpidr_el1()); } plat_ic_enable_interrupt(intr); }典型CMN700中断映射:
struct sgi_ras_ev_map sgi575_ras_map[] = { /* ERR S */ {SGI_SDEI_DS_EVENT_1, 45, SGI_RAS_INTR_TYPE_SPI}, /* FAULT S */ {SGI_SDEI_DS_EVENT_1, 46, SGI_RAS_INTR_TYPE_SPI}, /* FAULT NS */ {SGI_SDEI_DS_EVENT_1, 47, SGI_RAS_INTR_TYPE_SPI}, };5.2 错误探测与处理
错误探测函数关键逻辑:
int sgi_ras_cmn_probe_error(const struct err_record_info *info, int *probe_data) { uint64_t errgsr = 0; int errgsr_index = 0; /* 检查HNF安全组状态寄存器 */ FIND_ERROR_GENERATING_NODE_REGISTER(HNF, errgsr, errgsr_index) if (errgsr != 0) { int node_pos = 0, node_num = 0; FIND_ERROR_GENERATING_NODE_NUM(errgsr, errgsr_index, node_pos, node_num) *probe_data = hnf_node_base_offsets[node_num]; } return (errgsr != 0) ? 1 : 0; }中断处理函数实现要点:
int sgi_ras_cmn_intr_handler(const struct err_record_info *err_rec, int probe_data, const struct err_handler_data *const data) { /* 1. 记录错误详情 */ print_error_registers(probe_data); /* 2. 清除错误状态 */ uint64_t status = mmio_read_64(CMN_BASE + probe_data + ErrStatus); mmio_write_64(CMN_BASE + probe_data + ErrStatus, status); /* 3. 禁用错误注入 */ mmio_write_64(CMN_BASE + probe_data + 0x3030, 0); /* 4. 通知上层系统 */ notify_standalone_mm(); notify_linux_kernel(); return 0; }6. Linux内核集成
6.1 用户空间错误注入
通过APEI EINJ接口触发错误注入:
# 查看可注入错误类型 cat /sys/kernel/debug/apei/einj/available_error_type # 注入可纠正内存错误 echo 0x8 > /sys/kernel/debug/apei/einj/error_type echo 1 > /sys/kernel/debug/apei/einj/error_inject6.2 错误处理流程优化
建议在内核侧实现以下增强:
- 错误信息解析:解析SCMI传递的错误详情
- 错误抑制策略:根据错误频率实施节流
- 系统健康报告:集成到内核事件通知框架
典型错误处理驱动结构:
static struct einj_driver cmn700_driver = { .name = "cmn700_ras", .error_types = EINJ_MEM_CORRECTABLE | EINJ_MEM_UNCORRECTABLE, .ops = { .error_inject = cmn700_error_inject, .error_clear = cmn700_error_clear, }, };7. 验证与调试技巧
7.1 系统级验证步骤
- 基础功能测试:
# 注入可纠正错误并检查日志 dmesg -w & echo 0x8 > error_type; echo 1 > error_inject # 验证错误计数器递增 cat /sys/devices/system/edac/mc/mc0/ce_count- 压力测试脚本示例:
import os def stress_test(inject_type, count): for i in range(count): with open('/sys/kernel/debug/apei/einj/error_type', 'w') as f: f.write(str(inject_type)) os.system('echo 1 > /sys/kernel/debug/apei/einj/error_inject') time.sleep(0.1)7.2 常见问题排查
- 错误注入无效果检查清单:
- 确认SCP固件已正确加载cmn700_einj模块
- 验证ACPI EINJ表已正确注册
- 检查CMN700寄存器映射是否与芯片手册一致
- 确认中断路由配置正确(gicd_ispendr寄存器)
- 性能优化建议:
- 对频繁错误注入场景,采用批处理模式减少上下文切换
- 关键路径上禁用调试日志(通过FWK_LOG_LEVEL控制)
- 使用SCMI快速通道替代标准邮箱通信
8. 扩展应用场景
本方案可扩展支持:
- 多节点协同错误注入:通过SCMI广播机制实现
- 自动化测试框架集成:结合CI/CD系统实现回归测试
- 安全错误注入:在TrustZone安全世界验证防御机制
未来可增强方向:
- 动态错误注入策略配置
- 错误传播路径追踪
- 机器学习驱动的自适应容错
