用 REA 逆开华为 BoF-XX 的性能模式:MateBook D15 2022 在 Linux 下的真 Turbo 解锁
本文摘要
华为 MateBook D15 2022(BoF-XX-PCB / M1010)在 Linux 下的 GNOME 性能模式只改 governor/EPP,mainline huawei-wmi 也没有 power_unlock。本文记录用 REA 检查出厂 huawei-wmi.ko、反编译 ACPI 的 WMAA/STUB/GTUB、定位 EC 的 TUBO/TDPS 与 AC/电量前置条件,并用 debugfs 原始 WMI 调用实现 huawei-perf,把 MMIO PL1 从 20W 提到 31W;同时给出 D-Bus/CLI/PPD 通用桥接、重启持久化、风扇与热限制的实测边界。
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再看桌面电源模式到底做了什么。
powerprofilesctl listls -l /sys/firmware/acpi/platform_profile* 2>&1cat /sys/devices/system/cpu/cpufreq/policy0/scaling_governorcat /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 到底暴露了什么。
出厂模块是压缩的,先解压再做校验。
zstd -df /lib/modules/$(uname -r)/kernel/drivers/platform/x86/huawei-wmi.ko.zst -o /tmp/huawei-wmi.kosha256sum /tmp/huawei-wmi.komodinfo /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 反编译。
mkdir -p /tmp/huawei-acpisudo cp /sys/firmware/acpi/tables/DSDT /tmp/huawei-acpi/sudo cp /sys/firmware/acpi/tables/SSDT* /tmp/huawei-acpi/cd /tmp/huawei-acpifor 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 时才真正分发。
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。
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 需要两次调用,所以工具内部最多重试三次。
#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,内核升级后仍然能用。
git clone https://github.com/xingwangzhe/huawei-turbo-mode.gitcd huawei-turbo-modemakesudo ./core/build/huawei-perf statussudo ./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 WEC[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 WEC[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。
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 20sudo ./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 曲线和手动控制边界 |
安装:
git clone https://github.com/xingwangzhe/huawei-turbo-mode.gitcd huawei-turbo-modemakesudo ./scripts/install.sh安装后可以直接用 GNOME/KDE 的电源模式,也可以走通用接口。
huawei-turbo-ctl statushuawei-turbo-ctl onhuawei-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 国际许可协议进行许可。
留言评论
评论资源加载失败,请重试。