summaryrefslogtreecommitdiff
path: root/2026/08/29/给一台Linux笔记本做服务器化改造:休眠、充电上限与EC寄存器考古/index.html
blob: 2420a461ab4854b736d1c8e82aed66dfc97aef29 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">

    <meta name="author" content="韩暮秋">
    
        
            <meta name="subtitle" content="暮秋小屋">
            
                
                    <meta name="description" content="这里是暮秋小屋,思念和灵感的寄存处">
                    
                        
                            <meta name="keywords" content="韩暮秋,MuqiuHan,'Muqiu Han', 'muqiu han', muqiuhan">
                            

                                

                                    <title>
                                        
                                            给一台 Linux 笔记本做服务器化改造:休眠、充电上限与 EC 寄存器考古 | 
                                                    暮秋小屋
                                    </title>

                                    
                                        
                                            <link rel="icon" href="/favicon.ico">
                                            

                                                        
                                                            
                                                                <!-- stylesheets list from _config.yml -->
                                                                
                                                                    <link rel="stylesheet" href="/css/style.css">
                                                                    
                                                                        

            
                <link rel="preload" href="/fonts/FZYouSongS-509R.woff2" as="font" type="font/woff2" crossorigin>

                                                                    
                                                                        
                                                                            <!-- scripts list from _config.yml -->
                                                                            
                                                                                <script
                                                                                    src="/js/menu.js"></script>
                                                                                
                                                                                    

                                                                                        
                                                                                            
                                                                                                <script
                                                                                                    src="https://polyfill.alicdn.com/polyfill.js?features=es6"></script>
                                                                                                <script
                                                                                                    id="MathJax-script"
                                                                                                    async
                                                                                                    src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/mathjax/3.2.0/es5/tex-mml-chtml.js"></script>
                                                                                                

                                                                                                    
                                                                                                        
  <meta name="generator" content="Hexo 6.3.0"></head>
  <body>
    <div class="wrapper">

      <div class="header">
  <div class="flex-container">
    <div class="header-inner">
      <div class="site-brand-container">
        <a href="/">
          
              暮秋小屋
                
        </a>
      </div>
      <div id="menu-btn" class="menu-btn" onclick="toggleMenu()">
        菜单
      </div>
      <nav class="site-nav">
        <ul class="menu-list">
          
            
                <li class="menu-item">
                  <a href="/">
                    主页
                  </a>
                </li>
                
                  
            
                <li class="menu-item">
                  <a href="/categories/gallery/">
                    日记本
                  </a>
                </li>
                
                  
            
                <li class="menu-item">
                  <a href="/tags/Medicine/">
                    泛医学
                  </a>
                </li>
                
                  
            
                <li class="menu-item">
                  <a href="/tags/Technique/">
                    计算机
                  </a>
                </li>
                
                  
            
                <li class="menu-item">
                  <a href="/tags/Life/">
                    生活
                  </a>
                </li>
                
                  
            
                <li class="menu-item">
                  <a href="/archives">
                    全部
                  </a>
                </li>
                
                  
            
                <li class="menu-item">
                  <a href="/about">
                    关于
                  </a>
                </li>
                
                  
                    
                      <li class="menu-item">
                        <a href="/search">搜索</a>
                      </li>
                      
        </ul>
      </nav>
    </div>
  </div>
</div>


      <div class="main">
        <div class="flex-container">
          <article id="post">

  
    <div class="post-head">
    <div class="post-info">
        <div class="tag-list">
            
                
                    <span class="post-tag">
                        <a href="/tags/Technique/">
                            Technique
                        </a>
                    </span>    
                
                    <span class="post-tag">
                        <a href="/tags/Linux/">
                            Linux
                        </a>
                    </span>    
                           
            
        </div>
        <div class="post-title">
            
            
                给一台 Linux 笔记本做服务器化改造:休眠、充电上限与 EC 寄存器考古
            
            
        </div>
        <span class="post-date">
            Aug 29, 2026
        </span>
    </div>
    <div class="post-img">
        
            <div class="h-line-primary"></div>
              
    </div>
</div>
    <div class="post-content">
    <p>一台退役笔记本装载 Hermes Agent 后转为 7×24 常驻服务器,出厂的笔记本形态留下三类故障:拔电半小时后自动休眠、合盖即失联、电池持续充至满电。本文记录改造全程。前两项属常规配置;第三项在 Linux 下无现成驱动,需要从主板固件代码中定位充电上限的寄存器地址并写入硬件,构成本文主体。</p>
<p>改造对象为 Redmi Book Pro 15 2022,系统 Pop!_OS 24.04,内核 6.18.7。关键步骤均附可复核证据,脚本清单见文末。</p>
<h2 id="自动休眠由两套机制叠加控制,仅修改其一必然复发"><a class="header-anchor" href="#自动休眠由两套机制叠加控制,仅修改其一必然复发">¶</a>自动休眠由两套机制叠加控制,仅修改其一必然复发</h2>
<p>第一处位于 GNOME 会话。<code>org.gnome.settings-daemon.plugins.power</code> 键值组区分供电与电池两种策略,该机出厂默认前者为 <code>nothing</code>(不休眠)、后者为 <code>suspend</code>(30 分钟无操作休眠)。服务器拔电即失联,直接原因即电池策略。修改两条键值即可禁用:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">gsettings <span class="built_in">set</span> org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type <span class="string">&#x27;nothing&#x27;</span></span><br><span class="line">gsettings <span class="built_in">set</span> org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type <span class="string">&#x27;nothing&#x27;</span></span><br></pre></td></tr></table></figure>
<p>第二处位于 systemd-logind。不处理它,合盖仍触发 suspend。向 <code>/etc/systemd/logind.conf.d/</code> 写入 drop-in 配置:</p>
<figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[Login]</span></span><br><span class="line"><span class="attr">HandleLidSwitch</span>=ignore</span><br><span class="line"><span class="attr">HandleLidSwitchExternalPower</span>=ignore</span><br><span class="line"><span class="attr">HandleLidSwitchDocked</span>=ignore</span><br><span class="line"><span class="attr">AllowSuspend</span>=<span class="literal">no</span></span><br><span class="line"><span class="attr">AllowHibernation</span>=<span class="literal">no</span></span><br></pre></td></tr></table></figure>
<p><code>AllowSuspend=no</code> 与 <code>AllowHibernation=no</code> 使 suspend、hibernate 两个 systemd 目标在系统层不可达:任何组件发起休眠请求,systemd 直接拒绝执行。这一层保证无论用户会话如何变更,服务器不会被睡掉。</p>
<h2 id="充电上限配置存在两年,从未生效"><a class="header-anchor" href="#充电上限配置存在两年,从未生效">¶</a>充电上限配置存在两年,从未生效</h2>
<p>机器长期安装 TLP,配置如下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">START_CHARGE_THRESH_BAT0=20</span><br><span class="line">STOP_CHARGE_THRESH_BAT0=80</span><br></pre></td></tr></table></figure>
<p>电量仍充至 94%(衰减后的实际满充点)。TLP 自身不操作硬件,仅调用内核接口。内核标准接口为 <code>/sys/class/power_supply/BAT0/charge_control_end_threshold</code>;TLP 依次探测三种后端:<code>natacpi</code>(内核 ACPI 驱动直写)、<code>acpi_call</code>(第三方模块调用任意 ACPI 方法)、<code>sysfs</code>(上述标准接口)。三种后端在本机全部缺失:Redmi 未向内核提交任何 EC 驱动,<code>charge_control_end_threshold</code> 不存在,<code>acpi_call</code> 未安装。</p>
<p>TLP 对后端缺失的处理是静默降级:保留配置,不执行任何操作。此即&quot;设置了上限却充到满&quot;的完整成因。</p>
<h2 id="从-DSDT-定位-EC-充电上限字段"><a class="header-anchor" href="#从-DSDT-定位-EC-充电上限字段">¶</a>从 DSDT 定位 EC 充电上限字段</h2>
<p>内核不提供接口时,固件代码是唯一可靠的文档。ACPI 的 DSDT 表是主板固件的 AML 执行代码,用 <code>iasl</code> 反汇编后可检索:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cp</span> /sys/firmware/acpi/tables/DSDT /tmp/probe/</span><br><span class="line">iasl -d /tmp/probe/DSDT.bin</span><br></pre></td></tr></table></figure>
<p>反汇编产物中,<code>Device (EC0)</code> 即嵌入式控制器(Embedded Controller,主板上管理电池、键盘、风扇等底层事务的 8 位单片机)。EC 向 ACPI 暴露的字段表以 <code>Field (ERAM…)</code> 开头,逐项列出字段名与位宽。检索电池相关命名,得到关键结构:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">Offset (0x80),</span><br><span class="line">ACIN,   1,    // AC 已插电</span><br><span class="line">BTIN,   1,    // 电池在位</span><br><span class="line">BTST,   4,    // 电池状态</span><br><span class="line">ADPW,   8,</span><br><span class="line">BTSN,   16,   // 序列号</span><br><span class="line">BTDC,   16,   // Design Capacity 设计容量</span><br><span class="line">BTDV,   16,   // Design Voltage 设计电压</span><br><span class="line">BTFC,   16,   // Full Charge Capacity 满充容量</span><br><span class="line">BTTP,   16,</span><br><span class="line">BTCT,   16,   // Cycle Count 循环次数</span><br><span class="line">BTPR,   16,   // Present Capacity 当前容量</span><br><span class="line">BTVT,   16,   // Voltage 电压</span><br><span class="line">RSOC,   8,    // Relative State of Charge 电量百分比</span><br><span class="line">…                // 8 个状态位占满一个字节</span><br><span class="line">BTCC,   16,   // Charge Cap 充电上限</span><br><span class="line">BATM,   16,</span><br></pre></td></tr></table></figure>
<p>依据 ACPI Field 的位累积规则逐字段推算,各字段在 EC RAM 中的字节位置随之确定:电量百分比 RSOC 位于偏移 0x92,充电上限 BTCC 位于 0x95-0x96(16 位小端)。本机 EC RAM 经内存映射窗口 <code>0xFE0B0400</code> 可直接访问。</p>
<p>偏移推算须经读取验证。一次读出 32 字节,与系统已知值交叉比对:</p>
<table>
<thead>
<tr>
<th>EC 字段</th>
<th>读数</th>
<th>系统对照值</th>
<th>结果</th>
</tr>
</thead>
<tbody>
<tr>
<td>RSOC</td>
<td>89%</td>
<td>upower 实时 89%</td>
<td>命中</td>
</tr>
<tr>
<td>BTDC</td>
<td>4664 mAh × 15440 mV = 72.0 Wh</td>
<td>energy_full_design 72.012 Wh</td>
<td>命中</td>
</tr>
<tr>
<td>BTFC</td>
<td>3939 mAh → 60.8 Wh</td>
<td>energy_full 60.818 Wh</td>
<td>命中</td>
</tr>
<tr>
<td>BTFC/BTDC</td>
<td>84.5%</td>
<td>电池健康度 84.455%</td>
<td>命中</td>
</tr>
<tr>
<td>BTPR/BTFC</td>
<td>88.9%</td>
<td>与 RSOC 89% 自洽</td>
<td>命中</td>
</tr>
</tbody>
</table>
<p>五项独立指标全部命中,偏移表成立。读取同时得到一项内核长期隐瞒的数据:循环次数 1196 次——sysfs 的 <code>cycle_count</code> 一直报 0,真实值存于 EC。84.5% 的健康度对应一千二百次循环,衰减幅度在正常区间;80% 上限将显著延缓后续衰减。</p>
<p>首次读取时 BTCC 为 0:该字段从未被任何系统写入过。TLP 静默失效,小米管理软件从未在 Linux 侧运行,此字段空置两年。</p>
<h2 id="写入与开机固化"><a class="header-anchor" href="#写入与开机固化">¶</a>写入与开机固化</h2>
<p>写入是针对内存映射窗口的 2 字节操作:<code>dd</code> 定位 <code>0xFE0B0495</code>,写入小端编码的 80,读回确认。行为验证给出更强的证据:写入时电量 87%,高于上限,电池状态立即转为 Discharging;电量自然回落至 83% 后状态转为 Charging。EC 严格执行&quot;低于 80 充电、达到 80 停充&quot;。</p>
<p>EC 值可能在冷启动后复位,需开机重写。方案为 oneshot 型 systemd 服务(<code>WantedBy=multi-user.target</code>),执行 20 行 shell 脚本:写入、至多 5 次读回重试、结果写 syslog。验证一行命令:<code>journalctl -t ec-charge-limit</code>。</p>
<h2 id="方法论:固件代码是最后的文档"><a class="header-anchor" href="#方法论:固件代码是最后的文档">¶</a>方法论:固件代码是最后的文档</h2>
<p>TLP 配置存在而从未生效,根源在于用户态工具依赖内核 ABI 的覆盖面。内核接口缺失时,<code>acpi_call</code> 一类通用调用器是第一层补救,但其前提仍是 ACPI 层存在可调用的方法。DSDT 反汇编显示本机 EC 声明了 BTCC 字段,而全部 AML 代码均未读写它:小米将充电控制实现为纯 EC 寄存器协议,仅 Windows 侧私有驱动知晓协议细节。这一层 acpi_call 无法覆盖,剩余路径只有直接读写 EC RAM。</p>
<p>直接写 EC 有真实风险:该内存窗口同时被固件自身使用,写错偏移可能影响风扇、键盘背光乃至供电策略。本次风险控制依赖三项措施:写入前完成只读交叉验证,五项指标全部命中后才执行写入;写入范围收窄至 2 字节;写入后立即读回并观察充电行为。回滚与写入同路:向同一地址写 100 即恢复满充。</p>
<h2 id="改造结果"><a class="header-anchor" href="#改造结果">¶</a>改造结果</h2>
<ul>
<li>拔电、合盖均不休眠;suspend 目标在 systemd 层全局禁用</li>
<li>充电上限 80% 由 EC 强制执行,不依赖任何用户态进程存活</li>
<li>开机自动重写上限,冷启动不丢配置</li>
<li>电池档案:设计 72 Wh,满充 60.8 Wh(健康度 84.5%),循环 1196 次</li>
</ul>
<p>脚本归档:<code>setup-server-power.sh</code>(休眠禁用)、<code>acpi-probe-battery.sh</code>(DSDT 反汇编与只读探测)、<code>ec-verify-readonly.sh</code>(字段交叉验证)、<code>ec-set-charge-limit.sh</code>(写入与行为观察)、<code>power-rollback.sh</code>(一键回滚)。其他&quot;EC 沉默&quot;的机型可直接复用该流程:先以只读交叉验证证明地址表,再执行写入。</p>

</div> 

<script>
    window.onload = detectors();
</script>
    <div class="post-footer">
    <div class="h-line-primary"></div>
    <nav class="post-nav">
        <div class="prev-item">
           
        </div>
        <div class="next-item">
            
                <div class="icon arrow-right"></div>
                <div class="post-link">
                  <a href="/2026/08/25/%E9%9D%9E%E5%B0%8F%E7%BB%86%E8%83%9E%E8%82%BA%E7%99%8C%E9%B3%9E%E7%8A%B6%E5%88%86%E5%8C%96%E7%9A%84%E5%85%8D%E7%96%AB%E7%BB%84%E5%8C%96%E5%88%A4%E8%AF%BB%E5%8F%8A%E5%85%B6%E6%B2%BB%E7%96%97%E8%BE%B9%E7%95%8C/">Next</a>  
                </div>  
            
        </div>
    </nav>
</div>

    
      <div class="post-comment">

     

     
    
    

</div>
     
  
</article>
        </div>
      </div>
      
      <div class="footer">
    <div class="flex-container">
        <div class="footer-text">
            
                        
                            韩暮秋 |
                                
                                    
                                        希望路过的人可以添点柴火让这里暖和点
                                        
        </div>
    </div>
</div>


    </div>
    
    <script src="/js/mermaid-zoom.js"></script>

  </body>
</html>