<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Modem on Moo&#39;s Blog</title>
    <link>https://blog.moo.ac/tags/modem/</link>
    <description>Recent content in Modem on Moo&#39;s Blog</description>
    <image>
      <title>Moo&#39;s Blog</title>
      <url>https://blog.moo.ac/images/papermod-cover.png</url>
      <link>https://blog.moo.ac/images/papermod-cover.png</link>
    </image>
    <generator>Hugo -- 0.145.0</generator>
    <language>en</language>
    <copyright>PaperMod Contributors</copyright>
    <lastBuildDate>Fri, 25 Sep 2026 00:13:35 +0800</lastBuildDate>
    <atom:link href="https://blog.moo.ac/tags/modem/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>天邑TEWA-1108V光猫获取超密以及变砖修复过程</title>
      <link>https://blog.moo.ac/posts/fixbrickmodem/</link>
      <pubDate>Fri, 25 Sep 2026 00:13:35 +0800</pubDate>
      <guid>https://blog.moo.ac/posts/fixbrickmodem/</guid>
      <description>没想到居然有光猫改个不合法SN就无法启动系统了</description>
      <content:encoded><![CDATA[<h2 id="引言">引言</h2>
<p>家里的宽带最近升级了200M上行, 装维说200M上行必须得用XGPON光猫, 买了一个二手xgpon猫, 注册的时候卡在了29%, 过程是O3, 证明我的线路支持XGPON, 但是可能被ONU拦下了.</p>
<blockquote>
<p>参考itu的<a href="https://www.itu.int/epublications/publication/itu-t-g-987-3-2025-05-10-gigabit-capable-passive-optical-networks-xg-pon-transmission-convergence-tc-layer-specification?utm_source=chatgpt.com">说明</a>: ITU-T G.987.3 明确规定: ONU取得<strong>下行同步以后</strong>才进入 O2-3 Serial Number 状态; 在这个状态, ONU响应 OLT 的 Serial Number grant, 把自己的 SN 发给 OLT. 只有 OLT 随后发送一个与这个 SN 匹配的 <code>Assign_ONU-ID</code>, ONU才会进入 O4.</p></blockquote>
<img src="index.assets/image-20260926220853167.png" alt="image-20260926220853167" style="zoom:33%;" />
<p>于是想着是不是ONU侧认光猫的SN, 就TELNET上去修改了一下新光猫的SN为老光猫SN, 结果输入的时候忘了输入Vender ID, 本来12位的SN只输入了8位, reboot之后power灯一直闪烁, 插到电脑甚至显示网络线缆未连接.</p>
<p>于是乎就有了这篇救砖博客.</p>
<h2 id="获取超级密码和开启telnet">获取超级密码和开启Telnet</h2>
<p>博通方案的天邑TEWA-1108V在未成功注册前的超级密码为<code>nE7jA%5m</code>.</p>
<p>账号一般是useradmin.</p>
<p>使用超密登录后, 进入http://192.168.1.1:8080/enableTelnet.html</p>
<blockquote>
<p>根据恩山帖子 <a href="https://www.right.com.cn/forum/forum.php?mod=viewthread&amp;tid=8402465">https://www.right.com.cn/forum/forum.php?mod=viewthread&tid=8402465</a>
开启入口也可能是http://192.168.1.1/enableTelnet.html</p></blockquote>
<p>即可进入开启界面</p>
<p><img alt="image-20260926221713208" loading="lazy" src="/posts/fixbrickmodem/index.assets/image-20260926221713208.png"></p>
<p>开启TELNET后, 连接TELNET</p>
<blockquote>
<p>telnet可以不用su密码卡BUG获得root权限, 先按照常规方式登录Telnet, 打一个;回车, 这时会报错: sh: syntax error: unexpected &ldquo;;&rdquo;, 再输入命令echo $USER, 就是root权限</p>
<p>或者可以通过光猫的MAC地址计算出来, 计算器<a href="https://mao.trustavo.com/mac_a1">网址</a></p></blockquote>
<p>Telnet可用命令如下, <a href="https://www.right.com.cn/forum/thread-8428331-1-1.html">来源恩山</a></p>
<blockquote>
<h2 id="常用网页入口">常用网页入口</h2>
<table>
  <thead>
      <tr>
          <th>地址</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>http://192.168.2.1:8080/enableTelnet.html</td>
          <td>开启 Telnet</td>
      </tr>
      <tr>
          <td>http://192.168.2.1:8080/backupsettings.conf</td>
          <td>下载配置</td>
      </tr>
      <tr>
          <td>http://192.168.2.1:8080/updatesettings.html</td>
          <td>上传配置</td>
      </tr>
      <tr>
          <td>http://192.168.1.1:8080/NN_WAN1.html</td>
          <td>删除 TR069</td>
      </tr>
  </tbody>
</table>
<h2 id="nvram--地区相关">NVRAM / 地区相关</h2>
<table>
  <thead>
      <tr>
          <th>命令</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>ls /etc/default_configs</code></td>
          <td>查询地区配置</td>
      </tr>
      <tr>
          <td><code>cat /proc/nvram/LocationCode</code></td>
          <td>查看当前地区</td>
      </tr>
      <tr>
          <td><code>cat /proc/nvram/BaseMacAddr</code></td>
          <td>查询 MAC</td>
      </tr>
      <tr>
          <td><code>echo XX:XX:XX:XX:XX:XX &gt; /proc/nvram/BaseMacAddr</code></td>
          <td>修改 MAC</td>
      </tr>
      <tr>
          <td><code>echo 0x0B00 &gt; /proc/nvram/LocationCode</code></td>
          <td>切换地区，以北京为例</td>
      </tr>
  </tbody>
</table>
<h2 id="地区代码">地区代码</h2>
<table>
  <thead>
      <tr>
          <th>地区</th>
          <th>代码</th>
          <th>地区</th>
          <th>代码</th>
          <th>地区</th>
          <th>代码</th>
          <th>地区</th>
          <th>代码</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>北京</td>
          <td><code>0x0B00</code></td>
          <td>内蒙古</td>
          <td><code>0x0F00</code></td>
          <td>黑龙江</td>
          <td><code>0x1700</code></td>
          <td>福建</td>
          <td><code>0x2300</code></td>
      </tr>
      <tr>
          <td>天津</td>
          <td><code>0x0C00</code></td>
          <td>辽宁</td>
          <td><code>0x1500</code></td>
          <td>安徽</td>
          <td><code>0x2200</code></td>
          <td>江西</td>
          <td><code>0x2400</code></td>
      </tr>
      <tr>
          <td>河北</td>
          <td><code>0x0D00</code></td>
          <td>吉林</td>
          <td><code>0x1600</code></td>
          <td>浙江</td>
          <td><code>0x2100</code></td>
          <td>重庆</td>
          <td><code>0x32005</code></td>
      </tr>
      <tr>
          <td>山西</td>
          <td><code>0x0E00</code></td>
          <td>上海</td>
          <td><code>0x1F00</code></td>
          <td>江苏</td>
          <td><code>0x2000</code></td>
          <td>四川</td>
          <td><code>0x3300</code></td>
      </tr>
      <tr>
          <td>山东</td>
          <td><code>0x2500</code></td>
          <td>河南</td>
          <td><code>0x2900</code></td>
          <td>湖北</td>
          <td><code>0x2A00</code></td>
          <td>湖南</td>
          <td><code>0x2B00</code></td>
      </tr>
      <tr>
          <td>广东</td>
          <td><code>0x2C00</code></td>
          <td>广西</td>
          <td><code>0x2D00</code></td>
          <td>海南</td>
          <td><code>0x2E00</code></td>
          <td>贵州</td>
          <td><code>0x3400</code></td>
      </tr>
      <tr>
          <td>云南</td>
          <td><code>0x3500</code></td>
          <td>陕西</td>
          <td><code>0x3D00</code></td>
          <td>甘肃</td>
          <td><code>0x3E00</code></td>
          <td>青海</td>
          <td><code>0x3F00</code></td>
      </tr>
      <tr>
          <td>西藏</td>
          <td><code>0x3600</code></td>
          <td>宁夏</td>
          <td><code>0x4000</code></td>
          <td>新疆</td>
          <td><code>0x4100</code></td>
          <td></td>
          <td></td>
      </tr>
  </tbody>
</table>
<h2 id="qoecmd--mdm-常用命令">qoecmd / MDM 常用命令</h2>
<table>
  <thead>
      <tr>
          <th>命令</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>sh</code></td>
          <td>进入 Shell</td>
      </tr>
      <tr>
          <td><code>qoecmd help</code></td>
          <td>查看命令</td>
      </tr>
      <tr>
          <td><code>qoecmd mdm -h</code></td>
          <td>查看 MDM 命令用法</td>
      </tr>
      <tr>
          <td><code>qoecmd dumpmdm</code></td>
          <td>查看配置信息</td>
      </tr>
      <tr>
          <td><code>qoecmd dumpcfg</code></td>
          <td>查看可配置信息</td>
      </tr>
      <tr>
          <td><code>qoecmd gponSN get</code></td>
          <td>查看 GPON SN</td>
      </tr>
      <tr>
          <td><code>qoecmd gponSN set XXXXXXXXXXXX</code></td>
          <td>修改 GPON SN</td>
      </tr>
      <tr>
          <td><code>qoecmd telecomadmin get</code></td>
          <td>获取超级管理员密码</td>
      </tr>
      <tr>
          <td><code>qoecmd restoredefault</code></td>
          <td>恢复出厂设置</td>
      </tr>
      <tr>
          <td><code>qoecmd Register set status 0 result 1</code></td>
          <td>ITMS 注册</td>
      </tr>
      <tr>
          <td><code>qoecmd bobdata dump</code></td>
          <td>查看光模块型号</td>
      </tr>
      <tr>
          <td><code>ct_getITMSRegistStat</code></td>
          <td>查看 ITMS 注册状态</td>
      </tr>
      <tr>
          <td><code>cqct_itms</code></td>
          <td>查看 LOID</td>
      </tr>
      <tr>
          <td><code>mdm dumpobj 2</code></td>
          <td>查看配置</td>
      </tr>
      <tr>
          <td><code>mdm setpv</code></td>
          <td>修改配置</td>
      </tr>
      <tr>
          <td><code>mdm getpv InternetGatewayDevice.DeviceInfo.X_CT-COM_MACAddress</code></td>
          <td>查看 MAC</td>
      </tr>
      <tr>
          <td><code>mdm getpv InternetGatewayDevice.DeviceInfo.Manufacturer</code></td>
          <td>查看制造商</td>
      </tr>
      <tr>
          <td><code>mdm getpv InternetGatewayDevice.DeviceInfo.SerialNumber</code></td>
          <td>查看设备标识后十七位</td>
      </tr>
      <tr>
          <td><code>mdm getpv InternetGatewayDevice.DeviceInfo.ManufacturerOUI</code></td>
          <td>查看设备标识前六位</td>
      </tr>
      <tr>
          <td><code>mdm getpv InternetGatewayDevice.DeviceInfo.ProductClass</code></td>
          <td>查看设备型号</td>
      </tr>
      <tr>
          <td><code>mdm getpv InternetGatewayDevice.DeviceInfo.X_CT-COM_CheckKey</code></td>
          <td>查看 <code>X_CT-COM_CheckKey</code></td>
      </tr>
      <tr>
          <td><code>mdm getpv InternetGatewayDevice.DeviceInfo.X_CT-COM_ServiceManage</code></td>
          <td>查看 <code>X_CT-COM_ServiceManage</code></td>
      </tr>
  </tbody>
</table></blockquote>
<h2 id="变砖过程">变砖过程</h2>
<p>在光猫正常运行时, 通过 Telnet 查询 GPON SN:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">qoecmd gponSN get
</span></span></code></pre></div><p>返回值是 12 个文本字符, 形式类似:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gponsn:SCTY12345678
</span></span></code></pre></div><p>随后把它改成了只有 8 个十六进制字符的值, 并重启:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">qoecmd gponSN <span class="nb">set</span> A1B2C3D4 <span class="c1"># 不要执行!!!!!</span>
</span></span></code></pre></div><p>重启后的表现是:</p>
<ul>
<li>POWER 灯持续闪烁.</li>
<li>电脑直连时获取不到 DHCP 地址.</li>
<li>Web 管理页面无法访问.</li>
<li>手动指定地址后也无法 ping 通.</li>
</ul>
<p>reset之后依旧如此, 光猫的reset只能重置用户配置, 不会将所有设置重置.</p>
<p>后来的串口日志表明, 设备能够启动 U-Boot 和 Linux, 但在 PON 初始化期间出现 SN 校验错误, 随后 kernel panic, 等待 5 秒后再次启动.</p>
<h2 id="修复过程">修复过程</h2>
<p>本来以为80块钱打水漂变成电子垃圾了, 结果在冲浪时发现光猫可以通过ttl拯救, 但我手头没有usb-ttl设备, 但有一个Arduino Uno(Uno 兼容板, ATmega328P / 16 MHz, CH340 USB 串口). 接下来的操作都是通过Uno和光猫的ttl口连接操作的. 其中执行命令连接SIN的时候需要用1个2k欧姆的电阻和一个1k欧姆电阻分压实现3.3V输入.</p>
<h3 id="光猫拆解">光猫拆解</h3>
<p>光猫背面有两颗螺丝, 其中一颗</p>
<img src="index.assets/image-20260926223210580.png" alt="image-20260926223210580" style="zoom:25%;" />
<p>另一颗需要撕开标签</p>
<img src="index.assets/image-20260926223246811.png" alt="image-20260926223246811" style="zoom:25%;" />
<p>都拧下之后, 就可以硬撬外壳了, 外壳的卡扣很紧, 很难做到无损拆除请注意.</p>
<p>拆机后如下</p>
<img src="index.assets/image-20260926223424466.png" alt="image-20260926223424466" style="zoom: 33%;" />
<p>其中右下角有四个触点, 名字分别是</p>
<blockquote>
<p>SIN SOUT GND 3.3V</p></blockquote>
<p>其中SIN是写入, SOUT是对外输出, GND是接地.</p>
<h3 id="连接光猫ttl">连接光猫TTL</h3>
<p>我使用Arduino与其共地, 然后D2号口接受SOUT的信息转发给PC. 关键错误日志如下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="o">[</span>ERROR ploamFsm<span class="o">]</span> parseSNPW ,138:
</span></span><span class="line"><span class="cl">Invalid SN Number <span class="o">(</span>1<span class="o">)</span> <span class="o">([</span>A-Z<span class="o">]</span>4<span class="o">[</span>0-9a-fA-F<span class="o">]</span>8<span class="o">)</span>. Given: BRCM12345678
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>ERROR ploamFsm<span class="o">]</span> bcm_ploamCreate,443: parseSNPW failed.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Kernel panic - not syncing: Fatal exception
</span></span><span class="line"><span class="cl">Rebooting in <span class="m">5</span> seconds..
</span></span></code></pre></div><p>这里有一个容易误解的地方, BRCM12345678并不是我实际写入的那个SN, 应该只是个例子.</p>
<p>不过还能启动证明系统本身没坏, 只要想办法把SN改回来就能拯救.</p>
<h3 id="修复过程-1">修复过程</h3>
<p>根据博通官方的文档可以得知这款光猫的启动流程为</p>
<blockquote>
<p>Boot ROM → SPL → TPL → U-Boot proper → Linux → PON 初始化</p></blockquote>
<p>在出现以下标记时也就是<strong>SPL阶段</strong>立即发送小写 <code>a</code>, <strong>无需回车</strong>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="nv">$SPL</span>: 5.04L.02@348603 $
</span></span></code></pre></div><p>实际返回:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">Use enter to confirm input menu selection
</span></span><span class="line"><span class="cl">go - <span class="k">continue</span>
</span></span><span class="line"><span class="cl">ddr - mcb override<span class="o">(</span>hex<span class="o">)</span> or DDR safe mode.
</span></span><span class="line"><span class="cl">r - Boot Fallback<span class="p">;</span> <span class="m">1</span> - recovery
</span></span><span class="line"><span class="cl">bid - Ignore boardid <span class="k">while</span> booting
</span></span><span class="line"><span class="cl">mcb - List all the mcb selectors
</span></span><span class="line"><span class="cl">wdt - Disable spl wdt
</span></span><span class="line"><span class="cl">h - halt
</span></span></code></pre></div><p>发送 <code>h</code> 加回车后返回 <code>Halted</code>, 可以暂时停住启动. 后续菜单命令需要回车.</p>
<blockquote>
<p>这里参考了 Broadcom 的 <code>early_abort.c</code>.</p>
<p><a href="https://github.com/gnuton/asuswrt-merlin.ng/blob/master/release/src-rt-5.04axhnd.675x/bootloaders/u-boot-2019.07/board/broadcom/bcmbca/early_abort.c">Broadcom 参考 early_abort.c</a></p></blockquote>
<h3 id="spl-的-recovery-和-fallback-没解决问题">SPL 的 Recovery 和 Fallback 没解决问题</h3>
<p>实际测试结果:</p>
<table>
  <thead>
      <tr>
          <th>操作</th>
          <th>设备响应</th>
          <th>后续结果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>r 1</code>, 随后 <code>go</code></td>
          <td><code>Boot Image: Recovery</code></td>
          <td>仍加载 Image 2, 发生相同 SN 错误和 panic</td>
      </tr>
      <tr>
          <td><code>r</code>, 随后 <code>go</code></td>
          <td><code>Boot Image: Fallback</code></td>
          <td>选择 Image 1, 但 FIT 头认证失败</td>
      </tr>
  </tbody>
</table>
<p>备用镜像的报错:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">SELECTED Image <span class="m">1</span> FIT_VOL_ID is <span class="m">3</span>
</span></span><span class="line"><span class="cl">padding_pss_verify: invalid pss padding <span class="o">(</span>0xbc is missing<span class="o">)</span>
</span></span><span class="line"><span class="cl">FAILED to authenticate FIT header!!!
</span></span><span class="line"><span class="cl">resetting board...
</span></span></code></pre></div><p>此后热重启仍选择 Image 1; 断电约 5 秒再上电后, 重新回到可加载的正常镜像. 没有查明备用镜像认证失败的根因, 也没修改它.</p>
<h3 id="小写-ty-进入-u-boot-命令行">小写 ty: 进入 U-Boot 命令行</h3>
<p>以为没救了, 但翻输出日志看到这么一行</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">Enter specific characters to stop autoboot in  1s
</span></span></code></pre></div><p>尝试发送Ctrl+C, 26个字母10个数字都无效 .</p>
<p>Codex帮我在恩山的一篇帖子中的日志找到了 <code>Enter 'ty' to stop autoboot</code>, 因此试了试 <code>ty</code> , 成功看到 <code>=&gt;</code> 提示符. <a href="https://www.right.com.cn/forum/thread-8412122-1-1.html">天邑 TEWA-7100A 启动日志</a></p>
<p>具体就是按下ty, 不需要回车.</p>
<p>看到 <code>=&gt;</code> 后, 执行 <code>help</code>、<code>printenv</code> 均可正常返回.</p>
<h3 id="临时启动-linux-应急-shell">临时启动 Linux 应急 shell</h3>
<p>进入 U-Boot 后, 先查询:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="o">=</span>&gt; printenv bootcmd bootargs bootargs_append
</span></span><span class="line"><span class="cl"><span class="nv">bootcmd</span><span class="o">=</span>run once<span class="p">;</span>sdk boot_img
</span></span><span class="line"><span class="cl"><span class="c1">## Error: &#34;bootargs&#34; not defined</span>
</span></span><span class="line"><span class="cl"><span class="c1">## Error: &#34;bootargs_append&#34; not defined</span>
</span></span></code></pre></div><p>参考代码支持通过 <code>bootargs_append</code> 追加 Linux 启动参数. <a href="https://github.com/gnuton/asuswrt-merlin.ng/blob/master/release/src-rt-5.04axhnd.675x/bootloaders/u-boot-2019.07/arch/arm/mach-bcmbca/bcmbca-dtsetup.c">Broadcom 参考启动参数处理</a></p>
<p>本机正常可加载的是 Image 2, 因此执行:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">setenv bootargs_append <span class="nv">init</span><span class="o">=</span>/bin/sh
</span></span><span class="line"><span class="cl">printenv bootargs_append
</span></span><span class="line"><span class="cl">sdk boot_img <span class="m">2</span>
</span></span></code></pre></div><p>这里不执行 <code>saveenv</code>. 参数只用于这一次启动, 重启后消失.</p>
<p>Linux 返回:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">VFS: Mounted root <span class="o">(</span>squashfs filesystem<span class="o">)</span> <span class="nb">readonly</span> on device 254:0.
</span></span><span class="line"><span class="cl">Run /bin/sh as init process
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span></code></pre></div><p>这时已进入 root shell, 通常的初始化服务没有运行, 因此尚未触发 PON 崩溃路径.</p>
<h3 id="处理看门狗">处理看门狗</h3>
<p>第一次应急 shell 在约 80 秒后被看门狗重启. 重新进入后, 挂载必要的虚拟文件系统和内存目录, 再启动原厂喂狗程序:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">mount -t proc proc /proc
</span></span><span class="line"><span class="cl">mount -t sysfs sysfs /sys
</span></span><span class="line"><span class="cl">mount -t tmpfs tmpfs /var
</span></span><span class="line"><span class="cl">mkdir -p /var/run /var/tmp
</span></span><span class="line"><span class="cl">/bin/sh /etc/init.d/wdtd.sh start
</span></span></code></pre></div><p>这一顺序为守护进程提供可写的运行目录.</p>
<p>实际排查时, 在创建 <code>/var/run</code> 前启动 wdtd, 报了 PID 文件错误, 但进程仍持有 <code>/dev/watchdog</code>; 再次启动便提示 <code>EBUSY</code>.</p>
<p>不能看到 PID 文件错误就认为喂狗进程已经退出, 要检查实际进程.</p>
<h3 id="查询真正保存的-sn">查询真正保存的 SN</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">cat /proc/environment/gponsn
</span></span></code></pre></div><p>输出为误写的 8 位值. 本例该节点权限是只读:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">-r-------- ... /proc/environment/gponsn
</span></span></code></pre></div><p>直接运行 <code>qoecmd gponSN get</code> 返回:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">could not initialize msg, <span class="nv">ret</span><span class="o">=</span><span class="m">9002</span>
</span></span></code></pre></div><p>因为应急 shell 没有qoecmd所依赖的服务.</p>
<p>最终没通过 <code>qoecmd</code> 执行修复, 而是用 U-Boot 修改保存 SN 的环境变量.</p>
<h3 id="修改前-备份两个环境所在的-flash-块">修改前, 备份两个环境所在的 Flash 块</h3>
<p>其实这里不备份也没啥, 但是想着阴性结果也是结果, 就备份了一下.</p>
<p>U-Boot 查询环境位置:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="o">=</span>&gt; printenv env_boot_magic
</span></span><span class="line"><span class="cl"><span class="nv">env_boot_magic</span><span class="o">=</span>16384@0x40000,0xaf000
</span></span></code></pre></div><p>本机存在两个环境副本. Linux 的 <code>/proc/mtd</code> 显示:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">dev:    size     erasesize  name
</span></span><span class="line"><span class="cl">mtd0: <span class="m">10000000</span> <span class="m">00020000</span> <span class="s2">&#34;brcmnand.0&#34;</span>
</span></span><span class="line"><span class="cl">mtd1: <span class="m">00200000</span> <span class="m">00020000</span> <span class="s2">&#34;loader&#34;</span>
</span></span><span class="line"><span class="cl">mtd2: 0fd00000 <span class="m">00020000</span> <span class="s2">&#34;image&#34;</span>
</span></span></code></pre></div><p>环境副本分别落在以下 128 KiB 擦除块中:</p>
<table>
  <thead>
      <tr>
          <th>环境地址</th>
          <th>所在块起点</th>
          <th>对 <code>/dev/mtd1</code> 的读取参数</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>0x40000</code></td>
          <td><code>0x40000</code></td>
          <td><code>bs=131072 skip=2 count=1</code></td>
      </tr>
      <tr>
          <td><code>0xaf000</code></td>
          <td><code>0xa0000</code></td>
          <td><code>bs=131072 skip=5 count=1</code></td>
      </tr>
  </tbody>
</table>
<p>loader 分区从物理偏移 0 开始, 所以这里的分区内偏移与物理偏移相同. 其他分区布局不能直接套用.</p>
<p>只读 Flash, 写入内存中的 <code>/var/tmp</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">dd <span class="k">if</span><span class="o">=</span>/dev/mtd1 <span class="nv">of</span><span class="o">=</span>/var/tmp/env-block-before.bin <span class="nv">bs</span><span class="o">=</span><span class="m">131072</span> <span class="nv">skip</span><span class="o">=</span><span class="m">2</span> <span class="nv">count</span><span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl">sha256sum /var/tmp/env-block-before.bin
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">dd <span class="k">if</span><span class="o">=</span>/dev/mtd1 <span class="nv">of</span><span class="o">=</span>/var/tmp/env-block2-before.bin <span class="nv">bs</span><span class="o">=</span><span class="m">131072</span> <span class="nv">skip</span><span class="o">=</span><span class="m">5</span> <span class="nv">count</span><span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl">sha256sum /var/tmp/env-block2-before.bin
</span></span></code></pre></div><h3 id="经串口把备份传回电脑">经串口把备份传回电脑</h3>
<p>由于软件串口连续输出时会丢字节, 使用 OpenSSL Base64 编码, 并在每行之后短暂停顿:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="nb">echo</span> ENV_BACKUP_BEGIN
</span></span><span class="line"><span class="cl">openssl base64 -in /var/tmp/env-block-before.bin <span class="p">|</span> <span class="k">while</span> <span class="nv">IFS</span><span class="o">=</span> <span class="nb">read</span> -r L<span class="p">;</span> <span class="k">do</span> <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$L</span><span class="s2">&#34;</span><span class="p">;</span> usleep 5000<span class="p">;</span> <span class="k">done</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> ENV_BACKUP_END
</span></span></code></pre></div><p>第二份使用不同标记:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="nb">echo</span> ENV_SECOND_BEGIN
</span></span><span class="line"><span class="cl">openssl base64 -in /var/tmp/env-block2-before.bin <span class="p">|</span> <span class="k">while</span> <span class="nv">IFS</span><span class="o">=</span> <span class="nb">read</span> -r L<span class="p">;</span> <span class="k">do</span> <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$L</span><span class="s2">&#34;</span><span class="p">;</span> usleep 5000<span class="p">;</span> <span class="k">done</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> ENV_SECOND_END
</span></span></code></pre></div><p>电脑端把 RX 原始字节保存成 <code>.bin</code>, 截取开始、结束标记之间的 Base64, 再解码为 131072 字节的二进制文件. 两份文件的 SHA-256 都与光猫端计算值一致, 另外检查了环境内部 CRC.</p>
<p>这台精简系统里没有可用的 <code>printf</code> 命令, <code>sleep 0.05</code> 也不接受小数, 最后使用 <code>echo</code> 和 BusyBox 的 <code>usleep</code>.</p>
<p>备份的是<strong>故障时的环境块</strong>, 其中仍包含误写的 SN; 作用是保留修改前现场.</p>
<h3 id="在-u-boot-中恢复原始-sn">在 U-Boot 中恢复原始 SN</h3>
<p>备份完成后, 从应急 shell 执行 <code>reboot -f</code>, 重新在 U-Boot 倒计时处输入 <code>ty</code>.</p>
<p>先核对三项:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">printenv gponsn env_boot_magic bootargs_append
</span></span></code></pre></div><p>本次确认:</p>
<ul>
<li><code>gponsn</code> 仍是误写值.</li>
<li>环境副本位置与备份时一致.</li>
<li>临时的 <code>bootargs_append</code> 已经不存在.</li>
</ul>
<p>然后恢复<strong>自己设备记录过的原始 SN</strong>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="c1"># 以下 SN 是例子, 执行前必须换成自己设备的原值. </span>
</span></span><span class="line"><span class="cl">setenv gponsn SCTY12345678
</span></span><span class="line"><span class="cl">printenv gponsn
</span></span></code></pre></div><p>读回正确后, 保存:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="o">=</span>&gt; saveenv
</span></span><span class="line"><span class="cl">Saving Environment to BOOT_MAGIC... save to <span class="m">40000</span>
</span></span><span class="line"><span class="cl">save to af000
</span></span><span class="line"><span class="cl">OK
</span></span></code></pre></div><p>这里的 <code>saveenv</code> 会真正写入 Flash. 它保存当前环境, 因此在执行前确认临时启动参数没有混入非常重要. 参考实现会读入对应擦除块, 更新环境区域, 再写回整块; 前面的备份覆盖了两个实际涉及的块. <a href="https://github.com/gnuton/asuswrt-merlin.ng/blob/master/release/src-rt-5.04axhnd.675x/bootloaders/u-boot-2019.07/env/boot_magic.c">Broadcom 参考 BOOT_MAGIC 保存实现</a></p>
<p>执行 <code>reset</code>, 再次输入 <code>ty</code>, 再读一次:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="o">=</span>&gt; printenv gponsn bootargs_append
</span></span><span class="line"><span class="cl"><span class="nv">gponsn</span><span class="o">=</span>SCTY12345678
</span></span><span class="line"><span class="cl"><span class="c1">## Error: &#34;bootargs_append&#34; not defined</span>
</span></span></code></pre></div><p>这次读值发生在重新启动、重新加载环境之后, 因此验证了保存结果, 不只是修改后的 RAM 值.</p>
<p>最后执行:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">boot
</span></span></code></pre></div><p>恢复正常启动流程.</p>
<h2 id="修复结果与验证边界">修复结果与验证边界</h2>
<p>正常启动后, 看串口日志</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="o">[</span>NTC ploamFsm<span class="o">]</span> bcm_ploamCreate: ploam driver created.
</span></span><span class="line"><span class="cl">:::::Broadcom OMCI Stack v4.2 ...
</span></span></code></pre></div><p>并且POWER灯常亮, 光纤灯闪红(因为没插光纤).</p>
<h3 id="吐槽">吐槽</h3>
<p>谁家好人写光猫系统保存sn不校验, bootloader校验SN不通过导致kernel panic啊??</p>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><strong>Broadcom源码</strong>
<ul>
<li><a href="https://github.com/gnuton/asuswrt-merlin.ng/blob/master/release/src-rt-5.04axhnd.675x/bootloaders/u-boot-2019.07/board/broadcom/bcmbca/early_abort.c">early_abort.c</a></li>
<li><a href="https://github.com/gnuton/asuswrt-merlin.ng/blob/master/release/src-rt-5.04axhnd.675x/bootloaders/u-boot-2019.07/arch/arm/mach-bcmbca/include/early_abort.h">early_abort.h</a></li>
<li><a href="https://github.com/gnuton/asuswrt-merlin.ng/blob/master/release/src-rt-5.04axhnd.675x/bootloaders/u-boot-2019.07/board/broadcom/bcmbca/httpd/bcmbca_net.c">bcmbca_net.c</a></li>
<li><a href="https://github.com/gnuton/asuswrt-merlin.ng/blob/master/release/src-rt-5.04axhnd.675x/bootloaders/u-boot-2019.07/arch/arm/mach-bcmbca/bcmbca-dtsetup.c">bcmbca-dtsetup.c</a></li>
<li><a href="https://github.com/gnuton/asuswrt-merlin.ng/blob/master/release/src-rt-5.04axhnd.675x/bootloaders/u-boot-2019.07/env/boot_magic.c">boot_magic.c</a></li>
</ul>
</li>
<li><a href="https://www.right.com.cn/forum/thread-8412122-1-1.html">https://www.right.com.cn/forum/thread-8412122-1-1.html</a></li>
<li><a href="https://www.right.com.cn/forum/thread-8426017-1-1.html">https://www.right.com.cn/forum/thread-8426017-1-1.html</a></li>
<li><a href="https://www.right.com.cn/forum/thread-8455169-1-1.html">https://www.right.com.cn/forum/thread-8455169-1-1.html</a></li>
<li><a href="https://www.right.com.cn/forum/forum.php?mod=viewthread&amp;tid=8402465">https://www.right.com.cn/forum/forum.php?mod=viewthread&tid=8402465</a></li>
<li><a href="https://www.right.com.cn/forum/forum.php?mod=viewthread&amp;tid=8470716">https://www.right.com.cn/forum/forum.php?mod=viewthread&tid=8470716</a></li>
<li><a href="https://www.right.com.cn/forum/thread-8428331-1-1.html">https://www.right.com.cn/forum/thread-8428331-1-1.html</a></li>
</ol>
]]></content:encoded>
    </item>
  </channel>
</rss>
