起因

远程运维的机床,晚上加工完要能自己关机省电;风扇卡了转速掉零,报警得比冒烟早;客户报修,第一句先问"机器里是什么内存条"。这三件事落到系统上就是三个问题:电源怎么控制(软关机/重启到底写了什么寄存器)、风扇转速怎么读怎么控、内存容量和条目信息从哪来。教材不讲、教程跳过,这篇把账一次算清。

这三样东西在 PC 产业里都有年头:ACPI 是 1996 年 Intel、微软、东芝联手定的,接的是 BIOS 时代的烂摊子——那会儿节能靠 APM 黑盒、特性靠主板跳线;SMBIOS 早一年由 Intel 起草、现归 DMTF 维护,厂商把内存板卡信息写死在表里,省得每台机器都得拆开看;4 线 PWM 风扇规范最晚,2000 年代 Intel 才定下来,在那之前只有 3 线测速加电压调速的粗办法。

一、内存容量:三个信息源,各管一层

信息源 回答的问题 获取方式
UEFI 内存映射 系统实际可用多少内存 移交清单里现成(UEFI 篇已取)
SMBIOS/DMI 表 装了几根条、每根多大、频率、厂商、序列号 UEFI Configuration Table 按 GUID 取表指针
SPD(DIMM 上的 EEPROM) 内存条的原始出厂数据 SMBus 逐字节读,SMBIOS 够用时可跳过

可用容量:遍历 UEFI 内存映射,把 type == 7(EFI_CONVENTIONAL_MEMORY)区间的页数 ×4KB 累加——这是物理页分配器能看到的全部家底。

SMBIOS 操控:UEFI 系统表 Configuration Table 里按 GUID eb9d2d31-2d88-11d3-9a16-0090273fc14d 拿到入口(32 位入口签名 _SM_,64 位是 _SM3_,现代主板给哪个都能用)。入口结构里最要紧的是 Table Address 和 Structure Max Size——从 Table Address 开始就是一条结构链,遍历算法固定三步:

1
2
3
4
5
6
7
8
loop {
    let hdr = read::<SmbiosHdr>(p);        // type:u8, length:u8, handle:u16
    if hdr.type_ == END_OF_TABLE { break; }
    // 定长部分结束后是字符串区(\0 分隔),字符串区以连续两个 \0 收尾
    let strings_end = walk_strings(p + hdr.length as usize);
    handle(hdr);                           // 按 type 分发处理
    p = strings_end;                       // 下一个结构
}

跟内存相关的是 Type 16(物理内存阵列)和 Type 17(内存器件,每根条一条记录)。Type 17 常用字段的偏移(SMBIOS 3.x,定长部分):

偏移 字段 说明
0x0C Size(u16) 单位 KB;0x7FFF 表示真实值看 0x1A 的 ExtendedSize(MB)
0x12 MemoryType(u8) DDR4=0x1A、DDR5=0x22(SMBIOS 3.3+)
0x13 Speed(u16) MT/s
0x15 / 0x16 / 0x18 厂商 / 序列号 / 部件号 都是字符串表索引(1 起,0=空),按上面的字符串区数过去

工控自检页打印"2×16GB DDR4-3200 Samsung"就靠它;序列号记进资产台账,换件对账——二级经销商的机器档案从这条记录开始建。

二、电源控制:ACPI 的最小可用集

工控系统对电源的真实需求就三个动作:软关机(S5)、重启、以及"自己挂了能被拉起来"(看门狗)。前两个走 ACPI,最后一个走芯片组。

ACPI 的入口还是那套老路:移交的 RSDP → XSDT → FADT(签名 “FACP”)。FADT 里跟电源相关的字段(偏移按 ACPI 2.0+):

偏移 字段 用途
0x40 PM1a_EVT_BLK 电源事件寄存器块基址
0x48 PM1a_CNT_BLK 电源控制寄存器块基址
0x7C RESET_REG 复位寄存器地址(GAS 格式)
0x84 RESET_VALUE 往复位寄存器写的值

RESET_REG 那个"GAS 格式"值得展开一句,后面到处遇到:Generic Address Structure,12 字节 = 1B 地址空间(0=系统 IO、1=系统内存)+ 1B 位宽 + 1B 位偏移 + 1B 保留 + 8B 64 位地址。解析函数写一次,ACPI 里所有 GAS 字段(RESET_REG、PM1 寄存器的长度信息配合 PM1*_LEN 字段)都复用。

软关机:往 PM1a_CNT 写 SLP_TYP | SLP_EN——SLP_EN 是 bit13,SLP_TYP 是 bits10-12,最终写值 (s5_typ << 10) | (1 << 13)。S5 对应的 SLP_TYP 编码存在 DSDT 的 \_S5_ 对象里。完整解析 DSDT 得做 AML 虚拟机,没有这个必要,经典捷径是:DSDT 的 AML 字节流里直接搜 _S5_ 签名,后面跟的 Package 数据里取 SLP_TYP——几十行代码的事,早期的 Linux/开源引导都这么干。写完这个寄存器,主板电源时序切到 S5,整机下电。注意写完要Delay几毫秒再halt,让电源时序走完,别在时序中途去摸外设。

重启优先走 FADT 的 RESET_REG/RESET_VALUE(现代主板它就是 0xCF9 端口);兜底直接操作 0xCF9:先写 0x02(温复位)或 0x06→0x0E(完整冷复位序列),写完 CPU 当场重置。最后一道保险是故意触发三重故障让 CPU 复位——不优雅但永远有效。

硬件看门狗:Intel 芯片组内置 TCO 看门狗,入口链路:LPC/eSPI 桥(bus0:dev31:fn0)PCI 配置空间偏移 0x40 是 PMBASE,TCO 寄存器块就挂在 PMBASE+0x60 起的那片 I/O 区——喂狗就是往重载寄存器写一下,超时不喂由芯片组硬复位整机,CPU 软件死透了也拦不住它。工控价值在这:控制软件万一死循环,看门狗拉起机器回到安全态。基础篇先在 coop_scheduler 的主循环里喂狗(主循环卡死 = 不喂 = 复位),ISR 侧不喂——刻意让"协作层挂了就重启"成为系统行为。TCO 块内各子寄存器的位定义按芯片组 EDS 对,做成选配模块。

三、风扇与温度:Super I/O 加 CPU 数字传感器

风扇和主板温度的真相:它们不在 CPU 里、不在 ACPI 的简单寄存器里,而在主板的 Super I/O 芯片(Nuvoton NCT6775 系列、ITE IT87xx 系列是工控板两大常客)里。这类芯片管风扇转速计、PWM 输出、板载温度传感器,通过 ISA 风格的"索引/数据"端口对访问(如 0x2E/0x2F)。

以 NCT6775 为例的操控套路(寄存器细节照 Nuvoton 数据手册和 Linux nct6775 驱动对):

1
2
3
4
5
6
7
8
9
// 1. 进入扩展功能模式:向 0x2E 连写两次 0x87
// 2. 选逻辑设备:写索引 0x07(LDN)= 0x0B(硬件监控单元),
//    激活它的基址(LDN 0x60/0x61 可重映射,默认 0x290 一带)
// 3. 之后走芯片自己的"基址+索引/数据"对访问监控寄存器,
//    先写 0x4E 选 bank,再读写 0x4F——风扇计数、温度、PWM 都按 bank 分页
// 4. 转速读数:读风扇计数寄存器 → RPM = 1_350_000 / (count × divisor),
//    count 为 0xFF/0x00 都当"停转"处理,divisor 典型 2
// 5. PWM 控制:写占空比寄存器 0x00–0xFF(0=停转,255=全速)
// 6. 用完退出扩展模式(向 0x2E 写 0xAA),防误写

CPU 自身温度不用 Super I/O,走数字温度传感器(DTS):rdmsr(0x19C)(IA32_THERM_STATUS)的 bits16-22 是"距 TjMax 的温差",TjMax 从 rdmsr(0x1A2) 读——两者相减得真实核心温度。比任何主板探针都准。

控温策略(协作层一个 step 任务):温度-转速曲线用带迟滞的分段表(60℃ 以下低速、60-75℃ 中速、75℃ 以上全速,回落要低 5℃ 才降档,防振荡);过温联动安全:CPU 或功率器件超阈值 → 事件进状态回传队列 → 界面报警;超危急阈值 → 走安全联锁路径减速/停止加工——风扇策略在这里第一次和安全体系挂上钩。日志(exFAT)记录温度曲线,售后排障的硬证据。

四、汇总

你想知道/想做什么 这篇给的答案
实际可用内存 UEFI 内存映射 type7 累加
内存条明细 SMBIOS Type 17(厂商/序列号/频率/容量)
软关机 FADT PM1a_CNT 写 SLP_TYP
重启 FADT RESET_REG 或 0xCF9 序列,三重故障兜底
死机自愈 TCO 看门狗,协作层主循环喂狗
风扇转速/PWM Super I/O(NCT6775 类)索引/数据端口
CPU 温度 MSR 0x19C + 0x1A2
控温 迟滞分段曲线 + 过温安全联动

总结

平台管理这块的"难"其实是"散":内存三个信息源各答一层(映射管可用、SMBIOS 管明细、SPD 兜底);电源是 ACPI 的最小集(FADT 三个字段 + _S5_ 提取捷径 + 0xCF9 复位序列)加 TCO 看门狗兜命;风扇温度在 Super I/O(NCT6775 的扩展模式/LDN/转速换算)和 CPU 的 DTS(两个 MSR 相减)。每一样单独都不大,攒起来就是一张工控整机的"硬件管理面"。跑通标志:自检页打印内存条明细和 CPU 温度,风扇随温升变速,远程指令能让整机软关机和重启。

参考链接