From 03d44302a56f0d391cab9c5a3c72ed489c35eb94 Mon Sep 17 00:00:00 2001
From: i-shm
改造对象为 Redmi Book Pro 15 2022,系统 Pop!_OS 24.04,内核 6.18.7。关键步骤均附可复核证据,脚本清单见文末。
+改造对象为 Redmi Book Pro 15 2022,系统 Pop!_OS 24.04,内核 6.18.7。所有脚本完整源码收录于正文。
第一处位于 GNOME 会话。org.gnome.settings-daemon.plugins.power 键值组区分供电与电池两种策略,该机出厂默认前者为 nothing(不休眠)、后者为 suspend(30 分钟无操作休眠)。服务器拔电即失联,直接原因即电池策略。修改两条键值即可禁用:
gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type 'nothing' |
第二处位于 systemd-logind。不处理它,合盖仍触发 suspend。向 /etc/systemd/logind.conf.d/ 写入 drop-in 配置:
第二处位于 systemd-logind。不处理它,合盖仍触发 suspend。向 /etc/systemd/logind.conf.d/99-server-nosuspend.conf 写入 drop-in 配置:
[Login] |
AllowSuspend=no 与 AllowHibernation=no 使 suspend、hibernate 两个 systemd 目标在系统层不可达:任何组件发起休眠请求,systemd 直接拒绝执行。这一层保证无论用户会话如何变更,服务器不会被睡掉。
上述配置对应脚本 setup-server-power.sh,其主体即本节三条命令加上 logind 重启,此处不再展开。
机器长期安装 TLP,配置如下:
START_CHARGE_THRESH_BAT0=20 |
电量仍充至 94%(衰减后的实际满充点)。TLP 自身不操作硬件,仅调用内核接口。内核标准接口为 /sys/class/power_supply/BAT0/charge_control_end_threshold;TLP 依次探测三种后端:natacpi(内核 ACPI 驱动直写)、acpi_call(第三方模块调用任意 ACPI 方法)、sysfs(上述标准接口)。三种后端在本机全部缺失:Redmi 未向内核提交任何 EC 驱动,charge_control_end_threshold 不存在,acpi_call 未安装。
TLP 对后端缺失的处理是静默降级:保留配置,不执行任何操作。此即"设置了上限却充到满"的完整成因。
+TLP 对后端缺失的处理是静默降级:保留配置,不执行任何操作。此即"设置了上限却充到满"的完整成因。补装 acpi-call-dkms 后重启 TLP,tlp-stat 报告 natacpi (system76_acpi) = inactive (laptop not supported),阈值接口依旧未出现——ACPI 层无方法可调,此路不通。
内核不提供接口时,固件代码是唯一可靠的文档。ACPI 的 DSDT 表是主板固件的 AML 执行代码,用 iasl 反汇编后可检索:
cp /sys/firmware/acpi/tables/DSDT /tmp/probe/ |
反汇编产物中,Device (EC0) 即嵌入式控制器(Embedded Controller,主板上管理电池、键盘、风扇等底层事务的 8 位单片机)。EC 向 ACPI 暴露的字段表以 Field (ERAM…) 开头,逐项列出字段名与位宽。检索电池相关命名,得到关键结构:
Offset (0x80), |
依据 ACPI Field 的位累积规则逐字段推算,各字段在 EC RAM 中的字节位置随之确定:电量百分比 RSOC 位于偏移 0x92,充电上限 BTCC 位于 0x95-0x96(16 位小端)。本机 EC RAM 经内存映射窗口 0xFE0B0400 可直接访问。
偏移推算须经读取验证。一次读出 32 字节,与系统已知值交叉比对:
+偏移推算须经读取验证。验证脚本用 dd 从 /dev/mem 读出窗口内 32 字节,再按偏移表逐字段解码:
|
一次读出 32 字节,与系统已知值交叉比对:
|
脚本包含三重保护:参数限制在 20-100;写入后读回比对,不匹配则重试;至多 5 次,失败写 syslog 并以非零码退出。服务单元 5 行:
+[Unit] |
部署后验证:journalctl -t ec-charge-limit 应含 BTCC set to 80 (attempt 1)。
TLP 配置存在而从未生效,根源在于用户态工具依赖内核 ABI 的覆盖面。内核接口缺失时,acpi_call 一类通用调用器是第一层补救,但其前提仍是 ACPI 层存在可调用的方法。DSDT 反汇编显示本机 EC 声明了 BTCC 字段,而全部 AML 代码均未读写它:小米将充电控制实现为纯 EC 寄存器协议,仅 Windows 侧私有驱动知晓协议细节。这一层 acpi_call 无法覆盖,剩余路径只有直接读写 EC RAM。
直接写 EC 有真实风险:该内存窗口同时被固件自身使用,写错偏移可能影响风扇、键盘背光乃至供电策略。本次风险控制依赖三项措施:写入前完成只读交叉验证,五项指标全部命中后才执行写入;写入范围收窄至 2 字节;写入后立即读回并观察充电行为。回滚与写入同路:向同一地址写 100 即恢复满充。
@@ -6838,7 +6845,7 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的脚本归档:setup-server-power.sh(休眠禁用)、acpi-probe-battery.sh(DSDT 反汇编与只读探测)、ec-verify-readonly.sh(字段交叉验证)、ec-set-charge-limit.sh(写入与行为观察)、power-rollback.sh(一键回滚)。其他"EC 沉默"的机型可直接复用该流程:先以只读交叉验证证明地址表,再执行写入。
辅助脚本归档于本地(篇幅所限未全文收录):setup-server-power.sh(休眠禁用,含备份与逐步回显)、acpi-probe-battery.sh(DSDT 反汇编与 ACPI 只读探测)、ec-set-charge-limit.sh(首次写入与 60 秒行为观察)、power-rollback.sh(一键回滚)。其他"EC 沉默"的机型可直接复用核心流程:反汇编 DSDT 检索字段表 → 手工推算偏移 → 只读交叉验证 → 验证命中后再写入。