IIJの低圧向けスマートメーターBルート活用サービスが終了したらしく、機材がヤフオクに流れてきました。
この箱はアットマークテクノのArmadillo-Box WS1をちょっとカスタマイズ1したものです。
www.atmark-techno.com armadillo.atmark-techno.com
最近まったくRaspberry Pi Zero 2 Wが買えなくてつまらんので、メモリもCPUクロックも半分だけどしっかりしたドキュメント等必要なものがインターネットに置いてあって(この手の国産ハードにしては)めちゃくちゃ優秀なので、そんなに過酷なことにはならないと踏んで遊んでみることに。
ファームを初期化
SA-M0のファームが入ってるけどなんもできないので初心に戻します。バラして後述のようにシリアル接続し、JP2ショートして保守モード2でブートしてtftpでuserlandを投入3。
この写真あとで撮ったのでJP2オープンしてる。
tftpサーバはホストがWindows4なのでtftpd64を使いました。コマンド例はドキュメントにあるので略。
[root@abws1-0 (pts/1) ~]# cat /proc/version Linux version 3.14.36-at4-abws1-add-support-a420 (atmark@atde5) (gcc version 4.6.3 (Debian 4.6.3-14atmark1) ) #1 PREEMPT Tue Feb 23 16:45:53 JST 2016 [root@abws1-0 (pts/1) ~]# uname -a Linux abws1-0 3.14.36-at4-abws1-add-support-a420 #1 PREEMPT Tue Feb 23 16:45:53 JST 2016 armv5tejl GNU/Linux
シリアル接続について
デバイスから出てるUARTはTTLレベルでなくRS232Cレベルなので、計算機側がTTLレベルUARTだとレベルコンバータが必要です。
RS232CレベルのUARTを錬成した pic.twitter.com/fTYs34QHIX
— "にょる。" (@W53SA) 2023年4月7日
デバイス側はTxD(Pin5)/RxD(Pin3)/GND(Pin9)に出ている5のでよしなにUARTに繋いで115200bps6でシリアルターミナルを起動します。
当座の目標
とりあえず遊べることが第一目標なので、このラインを目標にします。
- sshで入れる
- UART接続は箱を閉じられないので辛い
- scpでファイルを出し入れできる
- USBメモリ抜き差しとmountは面倒
- socat使ってBP35A1とtcpで会話
- PuTTYで会話したり、ホスト側でsocatしてptyに飛ばしたりできるようになる
本当はuserlandをカスタムしてssh/scp/socatを入れるんだろうけど面倒なので、microSDを刺してpersistent storageを増やしbinaryを放り込む作戦で行きます。
eth0をよしなにして時刻を合わせる
userlandを入れた状態で起動するとeth0がDHCPに失敗してます。これは/etc/config/interface
で/etc/armsd/udhcpc.script
を読もうとして/etc/armsd
以下がないことによるのでまるごと取っ払います。
また、RTCついてないんでifup時にntpclient7で時間を合わせます。
[root@abws1-0 (pts/1) ~]# cat /etc/config/interfaces auto eth0 iface eth0 inet dhcp up ntpclient -i 1 -h ntp.nict.jp -s
NIClo
がないんでssh local portforwardingが出来ないんですけど、LANにしか繋がないので今回は気にしない。
ちなみにDHCPを使わずstaticにするときは /etc/config/interfaces
にNICの設定を書き、/etc/config/resolv.conf
にDNSレゾルバの設定を書きます8。
staticの場合の設定例
NICの設定
[root@abws1-0 (pts/1) ~]# cat /etc/config/interfaces auto eth0 iface eth0 inet static up ntpclient -i 1 -h ntp.nict.jp -s address 192.168.0.2 netmask 255.255.255.0 gateway 192.168.0.1
レゾルバの設定
[root@abws1-0 (pts/1) ~]# cat /etc/config/resolv.conf nameserver 192.168.0.1
/etc/config
のファイルを編集した後は第7章 コンフィグ領域 − 設定ファイルの保存領域 > 7.2. コンフィグ領域の保存にあるとおり、flatfsd -s
で保存する必要があります。
クロスコンパイル環境をつくる
クロスコンパイル環境構築済みVMイメージをメーカーが提供してくれているのでVMをいれるだけ。バージョンもドキュメントにある9とおり、v20150727以降のATDE5を拾ってきて入れます。VMware Playerはlatestの「VMware Workstation 17 Player 17.0.1 build-21139696」で動きました。
storageをつける
まだバラされている状態なので、microSDを刺します10。もし手元にUSBメモリがない場合は、ATDEがmicroSDを認識してる状態で次節のコンパイル済みバイナリを放り込んでから本体に刺することになる11ので適宜入れ替えてください。
microSDはATDEで12ext413フォーマットした上で、起動時に実行される/etc/config/rc.local
14で起動時にマウントさせます。ついでに/etc/profile
をいじってPATHを通しておきます。
mkdir /mnt/sd mount -t ext4 /dev/mmcblk0p1 /mnt/sd echo PATH=\$PATH:/mnt/sd/bin >> /etc/profile echo export PATH >> /etc/profile
ビルドする
クロスコンパイル環境ATDE5でやっていきます。
ssh
軽量sshのDropbear SSHを入れます。 github.com
CFLAGS='-DDEFAULT_PATH=\"/usr/bin:/bin:/mnt/sd/bin\" -DDEFAULT_ROOT_PATH=\"/usr/sbin:/usr/bin:/sbin:/bin:/mnt/sd/bin\" -DDROPBEAR_PATH_SSH_PROGRAM=\"/mnt/sd/bin/dbclient\"' ./configure --host=arm-linux-gnueabi
scp時にPATHが通っている場所にscp
やdbclient
がないと失敗するんですが、scpコマンドは非インタラクティブシェルで起動する15ので/etc/profile
をいじってもPATHが通りません。そこで、CFLAGSでセッション起動時のPATHを通します。前項でmicroSDマウントするpathを変えた場合はここのpathもよしなに変更してください。
make all
ではscp
はビルドされないので、make scp
でscpバイナリを別途ビルドする必要があります。
socat
何も設定しないでbuildしたら実行時にlibwrapとreadlineがないって怒られたのでdisableしておきます。
./configure --host=arm-linux-gnueabi --disable-libwrap --disable-readline
入れていくぞ
USBメモリ経由でさっきコンパイルした dropbear
/ scp
/ dropbearkey
/ socat
をPATH通したmicroSD上の/mnt/sd/bin
にコピっておきます。
ssh/scp
まず、hostkeyを生成します。ed25519だとATDEのsshが古くて認識しないのでrsaで生成しました。
/mnt/sd/bin/dropbearkey -t rsa -f /mnt/sd/etc/host.key
その上で/etc/config/rc.local
に設定を投入します。
# start ssh iptables -I INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT echo /bin/ash > /etc/shells /mnt/sd/bin/dropbear -r /mnt/sd/etc/host.key
userlandには/etc/shells
がなくgetusershell(3)がデフォルトの"/bin/sh,/bin/csh"
を返す一方でrootユーザのshellは/bin/ash
になっていてこのままではssh loginできないので/etc/shells
を雑に生成しています。
sshができるようになると、UART取っ払って箱を閉じることが出来ます。dhcpで拾うIPアドレスはdhcpサーバ側でMacアドレス見て固定したので、設定はここまで。
socat
ローカルでtty叩きたくなる日が来るかもしれないので、sshで入って必要に応じて起動できるようにしておく。
[root@abws1-0 (pts/1) ~]# cat /mnt/sd/bin/bp35a1 #!/bin/sh /mnt/sd/bin/socat -d -d tcp-l:23366,reuseaddr,fork file:/dev/ttymxc2,rawer,echo=0,waitlock=/var/run/ttymxc2.lock,b115200
これで23366/tcpにBP35A1が見えます。socatの引数について詳細はmanを参照してください。
最終的な設定
[root@abws1-0 (pts/1) ~]# cat /etc/config/rc.local #!/bin/sh . /etc/init.d/functions PATH=/bin:/sbin:/usr/bin:/usr/sbin # mount sd mkdir /mnt/sd mount -t ext4 /dev/mmcblk0p1 /mnt/sd echo PATH=\$PATH:/mnt/sd/bin >> /etc/profile echo export PATH >> /etc/profile # start ssh iptables -I INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT echo /bin/ash > /etc/shells /mnt/sd/bin/dropbear -r /mnt/sd/etc/host.key
簡単になんか走らせる
[root@abws1-0 (pts/1) ~]# cat /proc/cpuinfo processor : 0 model name : ARM926EJ-S rev 4 (v5l) BogoMIPS : 133.00 Features : swp half thumb fastmult edsp java CPU implementer : 0x41 CPU architecture: 5TEJ CPU variant : 0x0 CPU part : 0x926 CPU revision : 4 Hardware : Armadillo-420 Revision : 0300 Serial : 0000000000000000
というわけで、armv5lのarchでcross compileをGoやRustで試してみます。単にハロワするだけなので、より複雑な実装を書いていく場合に何が起きるかはまだ未確認。
Go
package main import "fmt" func main() { fmt.Println("hello,world") }
GOOS=linux GOARCH=arm GOARM=5 go build harowa.go
でbuildしたbinaryをscpしてシュッと実行できた。
Rust
stable-x86_64-pc-windows-msvc (default) rustc 1.69.0 (84c898d65 2023-04-16) で確認しました。
armv5te-unknown-linux
のtargetはgnueabi
とmusleabi
のふたつあります。
glibcに依存するarmv5te-unknown-linux-gnueabiだとATDE5でRustを走らせることになるけど、ATDE5に入ってるglibcがv2.13でRustは2.3116でビルドされてるので動かない。ちなみにx86_64-unknown-linux-musl
のrustup-initを直接17落としてATDE内で実行するとインストールはできるけど、rustc
が「error: command failed: 'rustc': No such file or directory (os error 2)」というめちゃくちゃつらいエラーを出して死ぬ。古すぎて何かがないんだろうけど、何がないかがそもそもわからない。
そこで、glibcに依存しないarmv5te-unknown-linux-musleabiをWindowsホストに入れて試してみる。
fn main() { println!("hello, world!"); }
rustc --target=armv5te-unknown-linux-musleabi -C linker=rust-lld ./harowa.rs
でビルド18して実行できた。
- 具体的にはRTCが実装されておらず、第8章 Linuxカーネル仕様 > 8.3.7. RTCに記述がある通りのエラーを起動時に吐いています。他にもカスタマイズされているところがあるかもしれないけど未確認。↩
- 起動モードのジャンパ設定は第14章 ハードウェア仕様 > 14.10. 起動モード設定ジャンパを参照。↩
- 第12章 フラッシュメモリの書き換え方法 > 12.4. TFTPを使用してフラッシュメモリを書き換えるを参照。↩
- Windows Firewallでtftp64.exeの通信を許可しても69/udpを蹴飛ばしてくれてちょっと大変だった。↩
- ピン配置は第14章 ハードウェア仕様 > 14.9. デバッグシリアルインターフェースを参照。↩
- デフォルトの速度については第8章 Linuxカーネル仕様 > 8.2. デフォルト起動オプションを参照。↩
-
ntpclientの引数
-i
はtimeoutを指定できて、defaultは600秒らしいんで1秒にしてあります。ntpclient時刻合わせで返ってこない | Armadilloサイトを参照。↩ - ネットワークの設定について詳細は第6章 動作確認方法 > 6.2.3. 有線LANを見てください。↩
- 第4章 Armadilloの電源を入れる前に > 4.2.1.2. ATDE5アーカイブの取得↩
- 第14章 ハードウェア仕様 > 14.8. microSDインターフェースを参照。↩
-
ホスト側で
/bin
ディレクトリを切って、そこに次節以降で生成するbinaryをコピーする感じ。↩ - ATDE上でUSBメモリ/SDカードのパーティションを作成・フォーマットする方法 | Armadilloサイト↩
-
microSDをホストで読む術がない場合は本体に挿して
mkfs.vfat
あたりでformatすることになりそう。userlandに入っているのはBusyBox v1.20.2なのでext2などのformatはできない。その場合は後段のmount時引数が変わります。↩ - 第9章 ユーザーランド仕様 > 9.2.4. /etc/config/rc.local↩
- セッション起動時のshell種別についてはssh,sftp,scp時のログインシェル、インタラクティブシェル - 朝から昼寝に詳しい記述があります。↩
-
https://github.com/cross-rs/crossで
armv5te-unknown-linux-gnueabi
のlibcが2.31になってるし、ビルドコンテナがubuntu:20.04
になっている。↩ - rustup-initのbinary一覧はOther Installation Methods - Rust Forge > Other ways to install rustupにあります。↩
-
rust-lld
をつけないと「error: linkercc
not found」でコンパイルが落ちます。rust-lld
はcargo-binutils入れなくても実行できました。↩