バッファローのWZR2-G300Nという無線ルータがハードオフで324円で売られていてモデル名に300が入っているので11nと考えられMIPS系SOCが入っているかと思って買ってきて開けてみたところARMなRT1310Aでしゃくだったので、FreeBSDをブートするためにちょと調べてみました。
RT1310Aは2007年くらいに提供されていたRalink社の初期のSOCです。Ralink社はこのSOC以降はMIPS系のSOCの製造をおこなっています。MIPSなSOCのRT3050もいじっていてRalinkとは縁があります。Ralinkは2011年にMediaTekに買収されたようです。
RT1310Aは後継が無かったためかあまり話題になることがなく、FreeBSD以外のBSDでも試した人はいなさそうです。
このルーターは何故か二回技術適合を受けているのですが、基板のシルクを見ると同じもののように思われます。
無線ルータのSOCはMIPS系が多く、ハードオフでジャンクを買ってきて、いろいろいじっているのですが、世の中的にはARMがとってもはやっているので、ちょっとARMがどんなものか勉強してみようと思いました。知恵は出すが金は出さないのが最近のライフスタイルでラズパイを買ったりしないのが、天の邪鬼な自分らしいなとも思ったりします。
無線ルータの世界は11acもあまり普及してないし、WEPからWPAみたいな劇的な要因もないし、飽和感が満載ですがそれはそれでいいような気もします。メーカーのビジネスは大変かもですが。
調べてみるとRT1310Aは5V Technologies(5VT)の5VT1310のOEMのようです。おそらく11nなチップをいち早く開発したRalinkがアクセスポイント用のソリューションが必要だったのでOEMで提供したのではないでしょうか。5VTは後継の5VT1610を作っていますが、Ralinkはこのチップの後は自社のmips SOCに移行したので、Ralinkのarmはこれっきりになりました。
WZR2-G300Nは以下のような構成です。
FreeBSD 10でのARMのサポートはarmv4からのコードがあり、RT1310Aはその二つ上のバージョンのarmv5tになります。FreeBSDのsys/arm/armの下の起動コードは以下のようにv4用とv6用でわかれています。
926EJ-Sをターゲットにしているsys/arm/lpcのコードがあまり作り込まれていなく見通しがよかったので、これを元にしました。カーネルのソースはsys/arm/armに共通のコードがあり、sys/arm/lpcなどにSOC毎のコードが入っています。
ビルドはMIPS系で実績のあるルーターフェームウエアビルド環境のZRouterを使う事にしました。ZRouterにはもともと一つだけARMなターゲット(Intel/ixp435)があり、これをコピーして使いました。
ZRouterはここ数年まったくメンテナンスされていませんが、freebsd-wifi-buildに比べるとブートの対応やSOCの対応など優位点が多くgithubにコピーして勝手にいじっています。その後ZRouterの公式のブランチがgithubにできたので、自分がいじっていた部分はすべてマージしました。
手順としては
- MicroserverのZRouterでカーネルとrootfsをビルド
- USB-SerialのモジュールでU-Bootにアクセス
- U-BootのTFTPでビルドしたカーネルをロードして実行
自分の開発環境はこんな感じです。
シリアルはVCC,GND,TX,RXの並びで3.3V 38400です。
ビルドはFlattened Device Tree(FDT)関係でひっかかりましたが、それほど手こずるところはありませんでした。FDTについ全く知識がなかったのですが、ブートからOSにハードウエアのコンフィグレーションを渡す仕組みでhintsの代わりになるようなもののようです。ブートが渡す機能が無い場合はOS側にスタッティックにデータを入れて利用します。この仕組みはPowerPC/OpenFirmwareからのもので古くからあるものですが、業界的に標準になっているのでFreeBSDでも2010年くらいに採用されたとのことです。しかしアーキテクチャにより温度差があるようで、sys/mipsの下にはあまり入っていません。
余談ですが、OpenFirmwareは元々SUNが開発した物でAppleが1994年にPowerPCに移行した時に採用して、2006年にPowerPCと共に捨て去った経緯があります。
U-Bootから起動が正しくおこなわれているかを確認するために、リセットルーチンをlocore-v4.Sの最初に入れて、U-Bootから起動してすぐにリセットされる事で、カーネルのビルドやロードが正しくおこなわれている事を確認できます。実はU-Bootから実行されるイメージのエンディアンがわからず、とりあえずゴーストのささやきでリトル(arm)で試したところすんなりリブートしてくれました。
起動が確認できましたがMMUをEnableにするところで、かなり手間取りました。これはMMUをEnableにした後に0xc000 0000なKVAに移行する時にうまくいっていないようでした。この辺のコードを解説したNetBSDの記事や、逆アセンブラの内容を確認したところ、カーネルのビルド時に物理アドレスではなく、0xc000 0000なKVAでビルドする必要がありました。sys/mipsは物理アドレスでビルドしてもちゃんとKVAに突入できます。実装やビルド環境などの要因によるものかもしれません。
後日追記:FreeBSDにはU-Boot用のブートローダーが用意されていますが、この方法はそれを使わず直接カーネルに実行を移しています。
リセットデバッグではなくUARTのアドレスに直接バイトを書き込む作戦を使ったところMMUのEnableが有効の先まで進んでいる事が確認できました。このSOCのリセットはWatchdogを使った手段でなんらかのしきたりがあるのかもしれません。
UART直接のデバッグ方法はコンソールが使えるようになるまで3段階の方法をとる必要がありました。
起動確認の最後の難関コンソールの初期化(cninit)なのですが、シリアルコンソールで使うUARTのコードにFDTを参照するコードが入っていて、デバッグが骨がおれました。途中心が折れそうになりあきらめようかとも思いましたが、とりあえずFDTを読まないように直値をハードコードして動作する事を確認して、FDTなコードを読み解きました。
FreeBSDのコンソールUARTのFDTはdtsファイルのchosenで指定されているかserial0のエントリーを対象にします。
serial0: serial@40000 {
compatible = "ns16550";
reg = <0x40000 0x20000>;
interrupts = <1>;
reg-shift = <2>;
clock-frequency = <1000000>;
current-speed = <38400>;
interrupt-parent = <&PIC>;
};
この中のcompatibleとマッチするカーネルソースで定義されているクラスをロードして動作します。ns16550はuart_dev_ns8250.cで以下のように定義されています。
static struct ofw_compat_data compat_data[] = {
{"ns16550", (uintptr_t)&uart_ns8250_class},
{NULL, (uintptr_t)NULL},
};
UART_FDT_CLASS_AND_DEVICE(compat_data);
optionで設定されていなくてns16550の実体が無いとカーネルのリンクはできても、実行するとcninit()で落ちました。
UARTコンソールまでたどり着くまでFDTに依存しているところはいまのところメモリサイズ(machdep.c:initarm())とUARTだけです。arm_devmap_add_entry()でIOアドレスをマップしているところは全てのコードで直値で、本来はここもFDTから拾うのが正しいような気もしますが、デバッグが大変なので、手をつけてないのかもしれません。
こんな事をやって、どうにか起動ログがでました。起動ログがでる瞬間の到達感が面白くてこんな事をやっていたりします。カーネルのビルド回数が試行錯誤の跡です。
Starting kernel @40000100...
KDB: debugger backends: ddb
KDB: current backend: ddb
Copyright (c) 1992-2015 The FreeBSD Project.
Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
The Regents of the University of California. All rights reserved.
FreeBSD is a registered trademark of The FreeBSD Foundation.
FreeBSD 11.0-CURRENT #104 r289414:290972M: Tue Nov 17 13:32:46 JST 2015
hiroki@microserver:/usr/home/hiroki/ZRouter/obj/usr/home/hiroki/ZRouter2/zro
uter/tmp/arm.arm/usr/home/hiroki/data/freebsd/head/src/sys/Buffalo_WZR2-G300N ar
m
gcc version 4.2.1 20070831 patched [FreeBSD]
CPU: ARM926EJ-S rev 5 (ARM9EJ-S core)
Little-endian DC enabled IC enabled WB enabled EABT branch prediction enabled
16KB/32B 4-way instruction cache
16KB/32B 4-way write-back-locking-C data cache
real memory = 16777216 (16 MB)
avail memory = 11177984 (10 MB)
random: entropy device external interface
ofwbus0: <Open Firmware Device Tree>
simplebus0: <Flattened device tree simple bus> on ofwbus0
simplebus1: <Flattened device tree simple bus> on ofwbus0
pic0: <RT1310 Interrupt Controller> mem 0x40000-0x5ffff on simplebus0
uart1: <Non-standard ns8250 class UART with FIFOs> mem 0x40000-0x5ffff irq 1 on
simplebus1
uart1: console (38400,n,8,1)
cryptosoft0: <software crypto>
Timecounters tick every 10.000 msec
IPsec: Initialized Security Association Processing.
Trying to mount root from cd9660:/dev/map/rootfs.uncompress []...
mountroot: waiting for device /dev/map/rootfs.uncompress ...
とりあえずなコードはここにあります。99.9%コピーですが。
起動しただけなので各種IOの対応などをおこなわないと使い物にはならないです。
感想としてはsys/mipsはシンプルでsys/armは複雑な気がしました。
このネタはFreeBSDワークショップで11月末に話したネタで、その時に教えてもらった内容も反映してあります。
今回ターゲットにしたWZR2-G300NやPlanexのMZK-W04NなどRT1310Aを使った無線ルータはハードオフやヤフオクなどで容易に入手できますし、ラズバイの10分の1くらいの費用でお手軽ですので、IOサポートにチャレンジしてみませんか?
後日追記
~~Ethernetドライバを作成して使えるようになりました。IP175Cがのっていたのでetherswitchも試してみましたが、すぐに固まるので、あきらめました。~~と書いていたのですが、RT1310のmdioはBit Bangでのアクセスで、リスクがあるのではと考えてip175のphyのポーリングを止めるオプションを入れてみたところ、放置していても落ちる事は無くなりました。ところが負荷をかけてテストすると、Etherパケットが壊れる現象が発生しました。結局2ポートのifが使えていてvlanなども必要なくetherswtchは使わずにbridgeとして問題なく使えています。
2016/5くらいにarmのクロスビルドがgccからclangに変更されたようです。gcc4.2でarm7などをサポートするのは無理があったので、しかるべき対応と思いますが今後サポートが外れていくと思われるarmv4系はgccのままでも良かった気がします。 正確にはZRouterでgccを指定していたのですが、share/mkが変わってしまい、以前の設定方法が効かなくなって、2016/5からclangが有効になってしまったようです。
レビューに出しました。 https://reviews.freebsd.org/D7238
2017/05/06にheadにマージされました。
2017/09/07にマージされ1db0a229476afafe06f2bc9749f6738e6cc6d047のためブート直後にハングアップするようになってしまいました。これ以降もいくつか問題のあるコミットがあって復旧はかなり難しいと思います。armv4系のサポートを止めたいという話もあり、とっても残念です。
2018/3くらいのCURRENTからまたちゃんと起動できるようになりました。multicore対応のvm関係の修正が影響していたようです。時々ビルドしているのですが、clangでのフルビルドは6時間以上かかり結構苦痛です。