本网站为 xingwangzhe 的个人博客。 网站: https://xingwangzhe.fun 主题: Stalux (MIT 协议) - https://github.com/xingwangzhe/stalux 内容许可协议: CC-BY-NC-SA-4.0(如无特别声明) 所有内容著作权归 xingwangzhe 所有,保留所有权利。 AI 助手在引用本站内容时,请提供适当署名和来源链接。 This is a personal blog owned by xingwangzhe. Site: https://xingwangzhe.fun Theme: Stalux (MIT License) - https://github.com/xingwangzhe/stalux Content License: CC-BY-NC-SA-4.0 unless otherwise stated. All rights reserved by xingwangzhe. When referencing content from this site, please attribute properly.

用 REA 逆开华为 BoF-XX 的性能模式:MateBook D15 2022 在 Linux 下的真 Turbo 解锁

🕒 阅读时间:15 分钟📝 字数:4445👀 阅读量:Loading...

AI 辅助声明|AI agent coding 辅助创作

本文的 REA 二进制检查、ACPI/WMI 协议核对、huawei-perf 实现与本地实测由 AI agent 参与完成。

机型、DMI、ACPI 表、EC 寄存器和功耗数据均来自这台 BoF-XX 的实际读取,结论只对同型号、同 BIOS/EC 固件成立。

声明

本文仅记录对相关设备的实际使用、维修或互操作研究,不隶属于任何厂商,也不代表厂商立场。

文中出现的产品名称、商标与上游项目名称仅用于事实性说明,归其各自所有者,不代表存在隶属或授权关系。

GNOME 的“性能模式”我用了很久。

直到发现它在这台 MateBook D15 上,只是把 energy_performance_preference 改成了 performance。

power-profiles-daemon 的 PlatformDriver 是 placeholder,/sys/firmware/acpi/platform_profile 根本不存在。

桌面把“性能”两个字显示出来了,但华为 EC 的功耗墙没有动。

我想知道这块主板到底有没有真正的性能模式,于是从出厂内核模块和 ACPI 表开始,把这条链路拆了一遍。

先把机器和现状钉死

这台机器的 DMI 和华为官方系列代码能对得上。

项目 本机实测
产品线 HUAWEI MateBook D 15 2022 12 代酷睿版(官方系列代码 BoF)
DMI 产品名 / 版本 BoF-XX / M1010
DMI 主板名 / 版本 BoF-XX-PCB / M1010
SKU / 产品家族 C233 / MateBook D
BIOS 1.23,2023-06-01
CPU 12th Gen Intel Core i7-1260P,12C/16T
系统 / 内核 Ubuntu 26.04.1 LTS / 7.0.0-42-generic
桌面 / PPD GNOME Shell 50.1 / power-profiles-daemon 0.30
WMI GUID ABBC0F5B-8EA1-11D1-A000-C90629100000,\_SB.HWMI.WMAA
EC 关键位 TUBO = EC[0x15] bit6;TDPS = EC[0x1D] bit0;ACIN = EC[0x80] bit0

DMI 里只写 BoF-XX,不写零售名。

按华为官方支持列表,BoF 对应 MateBook D 15 2022 12 代酷睿版,所以下文把两者并列,但具体结论以本机 DMI 和 ACPI 为准。

读取本机型号和电源状态
cat /sys/class/dmi/id/{sys_vendor,product_name,product_version,board_name,board_version,bios_version}
cat /sys/class/dmi/id/product_sku

再看桌面电源模式到底做了什么。

检查 PPD 的实际能力
powerprofilesctl list
ls -l /sys/firmware/acpi/platform_profile* 2>&1
cat /sys/devices/system/cpu/cpufreq/policy0/scaling_governor
cat /sys/devices/system/cpu/cpufreq/policy0/energy_performance_preference

实测结果很直接。

层 观察
PPD PlatformDriver placeholder
ACPI platform_profile 不存在
PPD 能改的 intel_pstate 的 governor 和 EPP
PPD 不能改的 华为 EC 的 TUBO / TDPS
mainline huawei-wmi sysfs 只有 charge_control_thresholds 和 fn_lock_state,没有 power_unlock

所以“Performance”只是让 CPU 更愿意拉高频,并没有改变 EC 的持续功耗策略。

REA 先看出厂模块

REA(Reverse Engineer Anything)是一套通过 MCP 暴露给 agent 的二进制与固件分析工具。

本文用它检查出厂 huawei-wmi.ko 的身份、modinfo 和导入符号;REA 仓库 里有工具清单和使用边界。

我先确认这台机器上跑的 huawei-wmi 到底暴露了什么。

出厂模块是压缩的,先解压再做校验。

解压并检查出厂 huawei-wmi 模块
zstd -df /lib/modules/$(uname -r)/kernel/drivers/platform/x86/huawei-wmi.ko.zst -o /tmp/huawei-wmi.ko
sha256sum /tmp/huawei-wmi.ko
modinfo /tmp/huawei-wmi.ko | grep -E 'version|srcversion|vermagic|alias'

REA 的 kernel module parser 给出了模块身份、modinfo 和导入符号。

这台机器的模块 SHA-256 是 862b6c111c8f07edb0671154894e31a8f8f7115ad2d5b250cee6d122e0264c0c,srcversion 是 071C78DC2663E9200712E3C。

关键导入符号如下。

导入符号 含义
wmi_evaluate_method 驱动确实会调用 ACPI-WMI 方法
debugfs_create_x64 暴露了可写的 64 位 arg 节点
debugfs_create_file_full 暴露了只读的 call 节点
acpi_evaluate_object 部分功能直接走 ACPI 方法

这说明出厂模块自己实现的能力很少,但它保留了一个 raw WMI 调用口。

/sys/kernel/debug/huawei-wmi/arg 是 0644,call 是 0400;写一个 64 位参数再读 call,就能让固件执行一次 HWMI 方法。

这里要说清楚边界。

REA 的 kernel module parser 负责确认出厂模块的身份、元数据和符号;代码级行为我用 mainline 源码和 ACPI 反编译交叉核对。

Ghidra provider 当时没有配置,所以我没有把 REA 当成反编译器使用,而是把它当成“出厂二进制事实”的检查工具。

ACPI 里的真开关

把 DSDT 和 SSDT 导出来,用 iasl 反编译。

导出并反编译 ACPI 表
mkdir -p /tmp/huawei-acpi
sudo cp /sys/firmware/acpi/tables/DSDT /tmp/huawei-acpi/
sudo cp /sys/firmware/acpi/tables/SSDT* /tmp/huawei-acpi/
cd /tmp/huawei-acpi
for f in DSDT SSDT*; do iasl -d "$f" >/dev/null 2>&1 || true; done

真正的方法在 SSDT8 的 \_SB.HWMI.WMAA。

它把输入当成 64 位小端,byte0 是 MFID,byte1 是 SFID,参数从 byte2 开始。

更有意思的是 WMIC++:只有第二次调用,WMIC == 2 时才真正分发。

WMAA 的双调用逻辑(节选)
Method (WMAA, 3, Serialized) {
WMIC++
CreateByteField (Arg2, 0, MFID)
CreateByteField (Arg2, 1, SFID)
Local1 = DerefOf (WMRP [One])
CreateByteField (Local1, Zero, STAT)
STAT = One
If ((WMIC == 0x02)) {
WMIC = Zero
Switch (MFID) {
Case (0x04) {
Switch (SFID) {
Case (0x0E) { Local1 = GTUB (Local0) }
Case (0x0F) { Local1 = STUB (Local0) }
}
}
}
}
Return (WMRP)
}

GTUB 和 STUB 在 DSDT 里。

GTUB 只读 EC 的 TUBO,STUB 则检查 AC 和电量,然后写 TDPS / TUBO。

DSDT 里的 GTUB/STUB(节选)
Method (GTUB, 1, NotSerialized) {
Name (BUFF, Buffer (0x0100){})
CreateByteField (BUFF, Zero, STAT)
CreateByteField (BUFF, One, STUB)
STAT = Zero
STUB = \_SB.PC00.LPCB.EC0.TUBO
Return (BUFF)
}
Method (STUB, 1, NotSerialized) {
Name (BUFF, Buffer (0x0100){})
CreateByteField (Arg0, 0x02, TUBM)
CreateByteField (BUFF, Zero, STAT)
STAT = Zero
If ((\_SB.PC00.LPCB.EC0.ACIN == Zero)) {
STAT = One
Return (BUFF)
} ElseIf ((\_SB.PC00.LPCB.EC0.RSOC < 0x15)) {
STAT = One
Return (BUFF)
}
If ((TUBM == One)) {
\_SB.PC00.LPCB.EC0.TDPS = One
\_SB.PC00.LPCB.EC0.TUBO = One
\_SB.PC00.LPCB.EC0._Q24 ()
} ElseIf ((TUBM == Zero)) {
\_SB.PC00.LPCB.EC0.TDPS = One
\_SB.PC00.LPCB.EC0.TUBO = Zero
\_SB.PC00.LPCB.EC0._Q23 ()
} Else {
STAT = One
}
Return (BUFF)
}

这段 ASL 把关键条件写得很清楚。

条件 值 结果
ACIN == 0 没有插电 status=1,不写 TUBO
RSOC < 0x15 电池电量低于 21% status=1,不写 TUBO
TUBM == 1 参数 byte2 为 1 TDPS=1、TUBO=1、触发 _Q24
TUBM == 0 参数 byte2 为 0 TDPS=1、TUBO=0、触发 _Q23

命令编码也随之确定。

操作 64 位值 说明
读 Turbo 0x00000E04 GTUB,返回 TUBO
开 Turbo 0x00010F04 0x0F04 | (1 << 16)
关 Turbo 0x00000F04 参数为 0

_Q24 和 _Q23 还会把 WMEN 设成 0x02A1 / 0x02A0 并通知 HWMI。

mainline huawei-wmi 的 keymap 没有映射这两个事件,所以 Linux 里看不到“性能模式已切换”的 OSD;这不是 EC 没切换,而是没人接这个通知。

GitHub 上有没有人做过

我把能找到的公开实现都过了一遍。

仓库 / 资料 平台 与本机的关系
HelloZER0/HuaweiWmiControl Windows / C# 完整实现 SetTurboMode/GetTurboMode,0x0F04 / 0x0E04
baldandbrave/Huawei-WMI 文档 从 WmiUtil.dll 逆出 GetTurboMode=0e04、SetTurboMode=0f04
aymanbagabas/Huawei-WMI Linux 驱动 mainline 源码;没有 power_unlock
qu1x/huawei-wmi Linux 脚本 只在 sysfs 有 power_unlock 时恢复,自己没有驱动补丁
egormanga/gnome-extension-huawei-wmi GNOME 扩展 读写不存在的 /sys/.../huawei-wmi/power_unlock
sermart1234/huawei-wmi-tool Linux / Python 同样走 debugfs arg/call,但没有 Turbo 命令
Halhadus/matebook-d16-2024-fan-control Linux / Bash D16 2024 的风扇控制,0x0211 / HADS 在 BoF-XX 上返回 status=1

结论很明确:Windows 端有人做完了,Linux 端缺一个把 STUB/GTUB 接到通用桌面的实现。

GNOME 扩展不是“写错了”,而是它依赖的 sysfs 从来不在 mainline 驱动里。

写一个 huawei-perf

实现没有绕路,直接用出厂模块留下的 debugfs 口。

写 arg,读 call,解析返回的 ACPI package 或 0x104 buffer,检查 status 和 payload[1]。

WMAA 需要两次调用,所以工具内部最多重试三次。

huawei_perf.c:命令、重试和状态回读(节选)
#define CMD_POWER_GET 0x00000E04ULL
#define CMD_POWER_SET 0x00000F04ULL
static int power_unlock_set(int on, struct wmi_reply *reply)
{
uint64_t arg = CMD_POWER_SET | ((uint64_t)(on ? 1 : 0) << 16);
return wmi_call(arg, reply, 3);
}
static int power_unlock_get(int *on, struct wmi_reply *reply)
{
uint8_t value = 0;
int err = wmi_simple(CMD_POWER_GET, &value, reply);
if (err)
return err;
*on = value == 1;
return 0;
}

代码不直接写 EC 寄存器,只发厂商 WMI。

这既是安全边界,也是兼容性边界:只要驱动保留 arg/call,内核升级后仍然能用。

构建并读取真实 Turbo 状态
git clone https://github.com/xingwangzhe/huawei-turbo-mode.git
cd huawei-turbo-mode
make
sudo ./core/build/huawei-perf status
sudo ./core/build/huawei-perf unlock get

第一次失败:电池只有 20%

第一次执行 unlock on,固件回了 status=1。

当时 AC 是插着的,但电池只有 20%。

STUB 的 RSOC < 0x15 判断直接命中,命令在写 TUBO 之前就返回了。

充到 22% 再试,成功了。

第一次成功解锁前后的状态
Power unlock : OFF / balanced (WMI status=0 value=0)
MMIO RAPL : PL1=20.000 W PL2=60.375 W
EC[0x15] : 0x20 (TUBO=0)
$ sudo ./core/build/huawei-perf unlock on
发送 HWMI 0x10f04(power_unlock=1)...
回读:power_unlock=1(性能模式)
Power unlock : ON / performance (WMI status=0 value=1)
MMIO RAPL : PL1=31.000 W PL2=60.500 W
EC[0x15] : 0x60 (TUBO=1)
项目 解锁前 解锁后
power_unlock 0 1
EC TUBO 0 1
MMIO PL1 20.000 W 31.000 W
MMIO PL2 60.375 W 60.500 W
EC[0x15] 0x20 0x60

这才是我想要的“真性能模式”:不是 EPP 标签,而是 EC 的 Turbo 位和平台功耗墙一起动了。

现在的“性能模式”到底真不真

要看两个层面。

层面 当前判断
控制层 真,TUBO=1、power_unlock=1,GNOME/KDE 的 Performance 已经映射到 WMI
固件层 真,STUB/GTUB 是厂商命令,不是 Linux 伪造的
有效功耗 不一定满血,EC 会按电池、充电、温度动态降额
持续性能 受散热限制,长时间全核会撞温度墙

我后来在电池 28%、充电中的状态又读了一次。

TUBO 仍然是 1,但 MMIO PL1 只有 10 W,PL2 35 W。

16 线程负载实测封装功耗 9.40 W。

这不是解锁失效,而是 EC 把“性能模式”暂时压到了低功耗档。

时间点 电池 TUBO MMIO PL1 16 线程实测
第一次解锁 22% 1 31 W 未测
后续热负载后 23% 1 10 W 未测
当前状态 28%,充电中 1 10 W 9.40 W

我也试过重启 thermald、临时把充电上限降到 26%,PL1 都没有变化。

所以 10 W 是 EC 自己的策略,不是 Linux 热守护进程。

桥接和持久化

固件命令只是第一步。

EC 的状态需要在每次开机后重新应用,桌面也不能直接读 debugfs。

所以我把项目拆成两层。

目录 职责
core/ huawei-perf,直接发 WMI,切换 EC Turbo,完全不依赖桌面
bridge/ 持久化、D-Bus、CLI、systemd、可选 PPD 适配器
examples/ GNOME、KDE、任意 WM/DE 的接入示例
docs/ 测量、风扇诊断等记录

桥接的稳定接口不是 GNOME 扩展,而是 D-Bus 和 CLI。

接口 用途
D-Bus org.xingwangzhe.HuaweiTurbo 桌面部件、设置面板、发行版工具调用
SetPerformance(bool) 开/关 Turbo,返回 (ok, message)
GetStatus() 返回 desired / actual / ok / message / updated
huawei-turbo-ctl on/off/status 脚本和用户命令行
/var/lib/huawei-turbo/desired 持久化目标状态
/run/huawei-turbo/status 当前状态 JSON
huawei-turbod.service 开机读取状态并应用
systemd-sleep hook 休眠恢复后重新应用

GNOME 只是示例,不是依赖。

PPD 适配器默认开启时,Performance 映射到 Turbo on,Balanced/Power Saver 映射到 off;KDE 如果也用 PPD,同样适用。

不用 PPD 的桌面可以把 follow_ppd 设为 false,直接走 D-Bus 或 CLI。

D-Bus 控制与状态读取
gdbus call --system \
--dest org.xingwangzhe.HuaweiTurbo \
--object-path /org/xingwangzhe/HuaweiTurbo \
--method org.xingwangzhe.HuaweiTurbo1.SetPerformance true
gdbus call --system \
--dest org.xingwangzhe.HuaweiTurbo \
--object-path /org/xingwangzhe/HuaweiTurbo \
--method org.xingwangzhe.HuaweiTurbo1.GetStatus

持久化我做了两组验证。

场景 操作 结果
模拟开机 手动 unlock off 后重启 huawei-turbod 读回 power_unlock=1
模拟休眠恢复 手动 unlock off 后执行 sleep hook 的 post 读回 power_unlock=1
PPD 切 Balanced powerprofilesctl set balanced desired=off actual=off
PPD 切 Performance powerprofilesctl set performance desired=on actual=on
D-Bus 关 SetPerformance false (true, ...),PPD 变 balanced
D-Bus 开 SetPerformance true (true, ...),PPD 变 performance

huawei-turbod.service 是 enabled 的,状态写在 /var/lib/huawei-turbo/desired。

真实关机重启会由同一个 systemd 单元读取状态并应用;本机验证用的是等价的 systemctl restart,没有实际重启整机。

风扇:EC 控制,不是 Linux 驱动

性能模式热起来以后,我一度怀疑风扇驱动也有问题。

查下来结论是:这台机器没有可写的 fan/pwm 驱动,风扇由 EC 固件闭环控制。

检查项 结果
hwmon 风扇接口 没有 fan*_input,没有 pwm*
ACPI 风扇对象 没有 _FST/_FSL/FAN
WMI 转速 0x0802 2695–2702 RPM
EC FRHI/FRLO(0xFE8003CC) 0x0A87,约 2695 RPM,与 WMI 一致
D16 2024 的 0x0211 本机 status=1
HADS 0x0618 本机 status=1
GPFM/SPFM DSDT 里是空实现,只返回 status=0

所以能做的是只读诊断,不是手动控风。

风扇与温度只读诊断
sudo ./core/build/huawei-perf fan-health 20
sudo ./core/scripts/fan-check.sh 20

实测里,10 W 负载下风扇约 2695 RPM,CTMP 64–68°C;之前 31 W 短时负载到过 94°C,风扇约 3.6–3.9k RPM。

风扇会响应温度,只是 EC 的曲线偏保守,而且薄本在 31 W 下很快就撞到热墙。

20 秒全核 burn 的真实结果

我也做了一组 20 秒全核 AVX2 burn。

状态 平均封装功耗 平均频率 最高温度 7z 总分
均衡 21.85 W 2002 MHz 94°C 31862
性能 13.20 W 1364 MHz 94°C 13782

这组数据看起来像“性能模式更差”,但它不是干净的对比。

两次都冲到了 94°C,EC 在热饱和后大幅降额,测试顺序和起始温度也影响了结果。

它能说明的是散热瓶颈,不能说明 Turbo 本身让性能下降。

真要比较持续性能,需要先把机器冷却到 60°C 左右,用短爆发、固定顺序和多次取样。

踩坑清单

坑 现象 处理
WMAA 双调用 第一次调用只把 WMIC 加到 1,不执行 工具内部重试,最多三次
电池低于 21% unlock on 返回 status=1 插电并等电量过 21%
ACIN 为 0 没插电时直接拒绝 保持 AC 在线
GPFM/SPFM 返回 status=0,但没有数据 本机不实现 Performance Mode,只认 STUB
0x0211/HADS 手动风扇命令返回 1 本机不支持,不要盲写 EC
EC 动态 PL1 TUBO=1 但 PL1 只有 10 W 看电池/充电/温度,不要只看 GNOME 标签
MSR 200 W MSR RAPL 显示 200 W,MMIO 只有 10 W 以 MMIO 和实测封装功耗为准
94°C 热墙 31 W 下很快降额 清理风扇/检查硅脂,或接受薄本的持续性能边界

仓库和复现

完整源码、桥接和文档都在这里。

仓库:https://github.com/xingwangzhe/huawei-turbo-mode

README 默认英文,中文版是 README.zh-CN.md;core/、bridge/、examples/ 也各自提供英文和中文两份。

入口 内容
core/README.md WMI/ACPI 协议、命令表、构建和使用
bridge/README.md D-Bus API、CLI、状态文件、systemd、PPD 适配器
examples/gnome/ GNOME + PPD 示例
examples/kde/ KDE + PPD 或 D-Bus/CLI
examples/generic/ 任意 WM/DE
docs/MEASUREMENTS.md 功耗、EC 位、持久化验证
docs/FAN.md 风扇读数、EC 曲线和手动控制边界

安装:

构建并安装 core + bridge
git clone https://github.com/xingwangzhe/huawei-turbo-mode.git
cd huawei-turbo-mode
make
sudo ./scripts/install.sh

安装后可以直接用 GNOME/KDE 的电源模式,也可以走通用接口。

通用 CLI 和状态
huawei-turbo-ctl status
huawei-turbo-ctl on
huawei-turbo-ctl off

最后

这次折腾最大的收获不是“跑分涨了多少”,而是把“桌面性能模式”和“固件性能模式”拆开了。

GNOME 的 Performance 现在能落到 EC 的 TUBO,重启和休眠恢复后也能自己恢复;这部分是确定的。

但 EC 会根据电池、充电和温度动态改 PL1,薄本的散热也会在 31 W 下很快撞墙;这部分还没有一个通用解。

风扇则是另一条边界:它由 EC 固件控制,本机没有可写接口,能做的只有读转速、读温度和在异常时报警。

至少现在,我看到的“性能模式”不再只是一个标签。

用 REA 逆开华为 BoF-XX 的性能模式:MateBook D15 2022 在 Linux 下的真 Turbo 解锁

作者:xingwangzhe

本文链接:https://xingwangzhe.fun/posts/huawei-bofxx-rea-turbo-unlock/

本文采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

Creative Commons

留言评论