MIPSなルータをいろいろハードオフで買ってきていじって いるが、ARMなルーターもたまにあって、手元のRT1310Aなルーターがあるので 、ちょっといじってみようかと思う。
ターゲットはバッファローのWZR2-G300NでブートはU-BootでFlash 4M/SDRAM 16Mで ある。CPUはARM926EJSでVersion5TE(ARM9Eファミリ)のようだ。 FreeBSDのソースツリーにはこのSOCのコードは無いがARM926EJSなコードを 参考に作ってみようかと思う。ハードの情報はデーターシートは入手できていないが GPLな公開されているコードや、既存のLinuxのログなどを参考にする。 いまのことろ他のBSD関係で試している人は見当たらない。
チップにもプリントしてあるがこのチップは5V Technologiesの5VT1310のOEMだと思う。 Ralinkは11nなチップセットをかなり早い時期に投入していて、無線のチップセット だけではビジネス的に難しいため、ARM SOCをOEMして提供したのではないかと考え られる。その後おそらく、買収などでMIPS SOCを作れるようになったので、 このチップ以降はMIPS SOCを提供するようになったのでは想像される。 5V Technologiesは製品があまり多くなく小規模な会社と思われる。 5VT1310は2005年くらいからあったようで、5VT9000_MPW1というコード名も使われて いたようだ。5VT1310を使った製品は以下のような物があったようだ。
BUFFALO | WZR2-G300N |
PCI | MZK-W04N |
Edimax | BR-6504N |
BELKIN | F5D8231-4 v3 |
AIRLINK | AR680W |
NetComm | NP740n |
D-Link | WBR-1310 |
PheeNet Technology | WLn-501 |
Sandbberg | Wireless N300 Router |
LG | |
Seednet | |
Yahoo! BB | |
NTT DoCoMo | ケータイホームシステム(宅内制御装置・家電制御アダプタ) |
UrJTAGで見てみたら以下のような情報が確認できたが、定義ファイルが無いので JTAGを使うのは難しいと思われる。
jtag> detect IR length: 4 Chain length: 1 Device Id: 00000111100100100110111100001111 (0x0000000007926F0F) Manufacturer: ARM Cannot open /Users/hiroki/Develop/Device/OpenSrouce/urjtag-0.10/urjtag/arm/PARTS Unknown part! chain.c(149) Part 0 without active instruction chain.c(200) Part 0 without active instruction chain.c(149) Part 0 without active instruction
実は新品を買って自宅で長らく使っている PlanexのMZK-W04Nも同じチップを使っていた。 あまり調子が良くないので、交換したいと思っているが。。。
WZR2-G300NのEther SwitchはIP175Cを使って いて、このコードはsys/dev/etherswitchにあります。MACが使えるようになればですが。
現在のsys/armの下のコードでARM926EJSがターゲーットになっている、lpc(NXP)と mv(Marvell)なのだがmvは作り込みが強く、作り込んでいないlpcを参考にすること にした。at91(Atmel)もARM9なコードのようだが、いろいろなチップに対応する コードになっていて複雑で参考にするには厳しい。
FreeBSD/armはARM922(ARM9ファミリ Version4)なarm/cavium/cns11xxが最低サポート ラインで、その上にARM926EJ-S(ARM9Eファミリ Version5TE) がある。lpc,mv,at91が このタイプになっている。この二つはarm/locore-v4.Sが起動時に最初に呼ばれて、 それ以外はlocore-v6.Sが呼ばれるようだ。これら以外はarm11かCortex Aとなる。 armはいろいろなバージョンがある上に呼び名がいろいろあって、たいへん分かりにくい。
ビルドはZRouterでおこなっていて、ZRouterに元々入っていたixpなターゲットを 参考にファイルを用意してみた。ixpはビックエンディアンだったんですが、 ゴーストがささやいてそうではない気がして、armebからarmに変更しました。 当たっていたのですが、簡単に確かめる方法ありますかね。。。ソースコードの方は ビルドの環境になるし、入っているLinuxでオペレーションできたとしてもコマンドが 限られていて良い方法が思いつきません。
コンパイルしていてfdt関係でエラーがでて、調べてみたら Flattened Device Tree という仕組みでhintsの代わりの仕組みになるようだ。sys/armの方にはかなり 取り入れているが、sys/mipsの方にはあまり取り入れられてないようだ。 カーネルビルド時にはsrc/sys/boot/fdt/dts/にあるファイルがdtcというツールで コンパイルされて、カーネルに入れられるようだ。
FDTはブートから渡されるデータを元にカーネルのデバイスコンフィグレーションを おこなう仕組みで、対応していないブートの場合はカーネル内にdtsをコンパイルした dtbファイルを入れてこれを元にコンフィグレーションをおこなうようだ。 元々OpenFirmware/Powerから出てきた仕様のようだ。昔MacがPowerPCだったころPCIな デバイスドライバを作っていて似たような仕様があったような記憶があるがもう10年 以上前の話で思い出せない。
まずはリブートデバッグで、U-Bootからちゃんと自分のコードに実行が移っているかを 確認してUARTがちゃんと使えるようになるようになれば良いのだが。
Marvellのポーティングは ここに資 料があった。FDTが入る前なので、少し現在とは変わってしまっているところもあるが。
ZRouterでビルドしたカーネルの逆アセンブラは以下のようにして出来る。
`find tmp/arm.arm -name objdump -type f | head -1` -d Buffalo_WZR2-G300N_kernel
ZRouterでビルドして先頭にリセットするコードを入れて確認していたところ、 案外簡単に起動までは確認できたが、MMUの設定でかなり手こずった。 NetBSDの起動処理を解説している rpi_start.Sを読む (第1回) などを参考にして理解してみた。
Buffalo_WZR2-G300N_kernel: file format elf32-littlearm Disassembly of section .text: c0000100 <_start>: c0000100: e1a09000 mov r9, r0 c0000104: e1a08001 mov r8, r1 c0000108: e1a0c002 mov ip, r2 c000010c: e1a0b003 mov fp, r3 c0000110: e10f7000 mrs r7, CPSR c0000114: e38770c0 orr r7, r7, #192 ; 0xc0 c0000118: e121f007 msr CPSR_c, r7 c000011c <disable_mmu>: c000011c: ee112f10 mrc 15, 0, r2, cr1, cr0, {0} c0000120: e3c2200d bic r2, r2, #13 ; 0xd c0000124: e3c22a01 bic r2, r2, #4096 ; 0x1000 c0000128: e3c22b02 bic r2, r2, #2048 ; 0x800 c000012c: ee012f10 mcr 15, 0, r2, cr1, cr0, {0} c0000130: e1a00000 nop (mov r0,r0) c0000134: e1a00000 nop (mov r0,r0) c0000138: e1a00000 nop (mov r0,r0) c000013c: ee120f10 mrc 15, 0, r0, cr2, cr0, {0} c0000140: e1a00000 nop (mov r0,r0) c0000144: e24ff004 sub pc, pc, #4 ; 0x4 c0000148 <Lunmapped>: c0000148: e28f0f41 add r0, pc, #260 ; 0x104 c000014c: eb000031 bl c0000218 <translate_va_to_pa> c0000150: e3a05101 mov r5, #1073741824 ; 0x40000000
当初sys/mipsと同じように物理アドレスでビルドしていたが、よくよくアセンブラ と逆アセンブラを読んでみたところ、ここら辺の値を使って仮想アドレスに移っていて 0xc000 0000でビルドしないとダメな事に気がついた。ただしU-Bootのヘッダーの方には 物理アドレスを指定している。
c0000254 <Lpagetable>: c0000254: c0000254 .word 0xc0000254 c0000258: c0348000 .word 0xc0348000 c000025c <Lreal_start>: c000025c: c0000100 .word 0xc0000100 c0000260 <Lend>: c0000260: c031bc24 .word 0xc031bc24 c0000264: c031bc24 .word 0xc031bc24 c0000268: c0345afc .word 0xc0345afc c000026c: c031e000 .word 0xc031e000
% file Buffalo_WZR2-G300N_kernel Buffalo_WZR2-G300N_kernel: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dyn amically linked (uses shared libs), for FreeBSD 11.0 (1100080), not stripped % file Buffalo_WZR2-G300N_kernel.kbin.oldlzma.uboot Buffalo_WZR2-G300N_kernel.kbin.oldlzma.uboot: u-boot legacy uImage, FreeBSD Kern el Image, Linux/ARM, OS Kernel Image (lzma), 1137320 bytes, Thu Nov 19 20:01:56 2015, Load Address: 0x40000100, Entry Point: 0x40000100, Header CRC: 0x8562228F, Data CRC: 0x56FF7484 % readelf -h Buffalo_WZR2-G300N_kernel ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: ARM Version: 0x1 Entry point address: 0xc0000100 Start of program headers: 52 (bytes into file) Start of section headers: 3317864 (bytes into file) Flags: 0x4000002, has entry point, Version4 EABI Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 5 Size of section headers: 40 (bytes) Number of section headers: 29 Section header string table index: 26
結局いろいろ試したがMMU設定後にはリセットのコードが動かなくなるようで、UART を使った確認に変更した。昔の会社の先輩直伝のmorimoridebugという関数で確認して みた。:)
I/Oのマップはarm_devmap_add_entry()でアドレスを登録して (最大32:AKVA_DEVMAP_MAX_ENTRIES)、arm_devmap_bootstrap() するとKVMの領域にマップされる。lpc_machdep.cを見ると、I/O空間の物理アドレスを arm_devmap_add_entryで渡して、元々の物理アドレスでbus_space_mapに 渡してbus_space_write_4でアクセスするようだ。
arm_devmap_add_entryは小さいアドレスから順に登録しないといきなり落ちます。
ほとんどのコードでarm_devmap_add_entryの設定値は直値になっているがfdtから 拾うべきなんだと思う。コンソールが使えるまでの処理は出来るだけ複雑にすべきでは ないと思うのですが、既にメモリサイズとUARTをFDTに依存しているので、バスの アドレスも入れてもいいとおもう。これによりソースファイルは修正しなくても dtsファイルの修正のみで同じタイプのSOCの起動は確認できるようになる。
コンソールが使えるようになる前のEARLY_PRINTFというデバッグ方法もあって、 sys/armではSOCDEV_VA,SOCDEV_PAでUARTをmapしておいてそのアドレスに直接データ を書き込んで確認できるようにするようだ。at91とかにはそのコードがあるようだ。 SOCDEV_PA,SOCDEV_VAは1Mバイトのアライメントで64Mバイトをマップするようだ。 結局は64Mバイトアラインということなのだが。下に書いてあるが、これはsetttb するまでしか使えない。
MMUを理解するために図を書いてみた。
文字を出力するようにしてデバッグしていたところsetttb()まで進んでいる事が 確認できた。ここでなぜ落ちるのかな。。。
setttbするとlocoreで一時的に設定したmapが効かなくなるのが理由だった。 bus_space_mapしてbus_space_write_1すればOKだった。
コンソールが設定されてシリアルコンソールが使えるようになった後のUARTは 入力は必要ないのと、割り込みはバスの初期化でコントローラが設定された 以降でないと使えないため、割り込みを使わずにビジーウェイトで動いている。
void morimoridebug(int c); void morimoridebug(int c) { uint8_t* uart_base_addr=(uint8_t*)0x1e840000; *(uart_base_addr) = c; }
void morimoridebug(int c); void morimoridebug(int c) { bus_space_tag_t bst; bus_space_handle_t bsh; bst = fdtbus_bs_tag; bus_space_map(bst, 0x1e840000, 0x100, 0, &bsh); bus_space_write_1(bst, bsh, 0, c); bus_space_unmap(bst, bsh, 0x100); }
cninit()のところで落ちる。いろいろ調べたらns8250の実体が無いのが原因だった。 __attribute__((weak))で宣言してあって、uart_ns8250がoptionで指定されていな かったことが原因と思われる。
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がカーネル内でUART_FDT_CLASS_AND_DEVICEで定義されて いるキーワードと一致している物を探す。下記はdev/uart/uart_dev_ns8250.c の定義で、上記のcompatibleと一致しているので、uart_ns8250_classが使われる。
static struct ofw_compat_data compat_data[] = { {"ns16550", (uintptr_t)&uart_ns8250_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data);
dtbの内容はdtcに含まれるfdtgetで確認できる。
% fdtget -l sample.dtb / % fdtget -p sample.dtb / % fdtget sample.dtb /ahb0@1E800000/serial@40000 compatible
0x40000000 | SDRAM | ||
0x1F000000 | 0x1F7FFFFF | Flash | |
0x19C00000 | 0x1E7FFFFF | AHB | |
0x19C00000 | 0x191FFFFF | Static/SDRAM Memory Controller | |
0x19C40000 | 0x19C5FFFF | Interrupt Controller | |
+0x00 | Intr Source Control Reg. 0 | ||
+0x80 | Intr Vector Reg. 0 | ||
+0x100 | IRQ Vector Reg. | ||
+0x104 | Intr Status Reg. | ||
+0x108 | Intr Pending Reg. | ||
+0x10c | Intr Mask Reg. | ||
+0x114 | Intr Enable Command Reg. | ||
+0x118 | Intr Clear Command Reg. | ||
+0x11c | Intr Set Command Reg. | ||
+0x124 | Test mode Reg. | ||
0x19C60000 | 0x19C7FFFF | AHB Arbiter | |
0x19C80000 | 0x19C9FFFF | MAC0 | |
0x19CA0000 | 0x19CBFFFF | MAC1 | |
0x19ce0000 | 0x1DCDFFFF(64M) | PCI MEMORY | |
0x1dce0000 | 0x1E4DFFFF(8M) | PCI IO | |
0x1e4e0000 | 0x1E6DFFFF(2M) | PCI BR | |
0x1E700000 | 0x1E71FFFF | DMA | |
0x1E800000 | 0x1EFFFFFF | APB | |
0x1E800000 | 0x1E81FFFF | Timer 0/1/2/3 | |
0x1E820000 | 0x1E83FFFF | RTC | |
0x1E840000 | 0x1E85FFFF | UART 0 | |
0x1E860000 | 0x1E87FFFF | UART 1 | |
0x1E880000 | 0x1E89FFFF | SPI | |
0x1E8A0000 | 0x1E8BFFFF | GPIO | |
0x1E8C0000 | 0x1E8DFFFF | WDT | |
0x1E8E0000 | 0x1E8FFFFF | SCU | |
0x1E940000 | 0x1E95FFFF | ACI0(PL040) | |
0x1E960000 | 0x1E97FFFF | ACI1(PL040) | |
|
|
なぜかMIPSなビルドに比べるとカーネルはあまり変わりないですが、ファイルシステム のisoの圧縮があまり小さくなっていません。
Buffalo_WZR2-G300N_kernel.kbin | 3074980 | 29% | ARM |
Buffalo_WZR2-G300N_kernel.kbin.oldlzma | 1061979 | ||
Buffalo_WZR2-G300N_rootfs_clean.iso | 11237376 | 38% | |
Buffalo_WZR2-G300N_rootfs_clean.iso.ulzma | 4311552 | ||
Planex_MZK-WNH_kernel.kbin | 3644076 | 29% | MIPS |
Planex_MZK-WNH_kernel.kbin.oldlzma | 1054388 | ||
Planex_MZK-WNH_rootfs_clean.iso | 12185600 | 25% | |
Planex_MZK-WNH_rootfs_clean.iso.ulzma | 3078144 |
なぜか圧縮されたカーネルがbootの下にあった事が原因だった。
Flashがprobeされるがデータが見えない状態で、調べたところCFIモードから復帰する 時に0xffを書いているのだが、0xf0(MXのデータシートではRESET)だとうまくいった。 CFIのドキュメントにも0xffか0xf0と書いてあって、どうするのがいいのかな。 ハンドパワーでCFIした結果
5VT1310-EVB# md 0x1f000000 1f000000: ea000016 e59ff014 e59ff014 e59ff014 ................ 1f000010: e59ff014 e59ff014 e59ff014 e59ff014 ................ 1f000020: 40380180 403801e0 40380240 403802a0 ..8@..8@@.8@..8@ 1f000030: 40380300 40380360 403803c0 deadbeef ..8@`.8@..8@.... 1f000040: 1e8e000c 19c04000 19c08000 19c09000 .....@.......... 1f000050: 40380000 40380000 4038c064 40394000 ..8@..8@d.8@.@9@ 1f000060: e10f0000 e3c000df e3800013 e129f000 ..............). 1f000070: eb000033 e24f007c e51f1030 e1500001 3...|.O.0.....P. 1f000080: 0a000007 e51f2038 e51f3038 e0432002 ....8 ..80... C. 1f000090: e0802002 e8b007f8 e8a107f8 e1500002 . ............P. 1f0000a0: dafffffb e51f005c e2400601 e2400c01 ....?.....@...@. 1f0000b0: e240d00c e51f0064 e2800004 e51f1068 ..@.d.......h... 1f0000c0: e3a02000 e5802000 e2800004 e1500001 . ... ........P. 1f0000d0: 1afffffb e51f008c e3a01101 e1500001 ..............P. 1f0000e0: 0a000009 e24f20ec e24f30b8 e0432002 ..... O..0O.. C. 1f0000f0: e2822004 e3a03101 e0822003 e4904004 . ...1... ...@.. 5VT1310-EVB# mw.w 0x1f0000aa 0x98 5VT1310-EVB# md 0x1f000000 1f000000: 0000ffff ffff0000 ffff0000 ffffffff ................ 1f000010: 0000ffff ffff0000 0000ffff 00000000 ................ 1f000020: 00520051 00020059 00400000 00000000 Q.R.Y.....@..... 1f000030: 00000000 00270000 00000036 00040000 ......'.6....... 1f000040: 000a0000 00050000 00040000 00160000 ................ 1f000050: 00000002 00000000 00070002 00200000 .............. . 1f000060: 003e0000 00000000 00000001 00000000 ..>............. 1f000070: 00000000 00000000 ffff0000 ffffffff ................ 1f000080: 00520050 00310049 00000031 00040002 P.R.I.1.1....... 1f000090: 00040001 00000000 c0b50000 0002c0c5 ................ 1f0000a0: 00000000 00000000 00000000 ffffffff ................ 1f0000b0: ffffffff ffffffff ffffffff ffffffff ................ 1f0000c0: ffff0000 ffff0000 ffffffff ffff0000 ................ 1f0000d0: ffff0000 ffff0000 ffff0000 ffff0000 ................ 1f0000e0: ffff0000 00000000 ffff0000 ffffffff ................ 1f0000f0: ffffffff ffff0055 ffff0055 0000ffff ....U...U....... 5VT1310-EVB# mw.w 0x1f000000 0xf0 5VT1310-EVB# md 0x1f000000 1f000000: ea000016 e59ff014 e59ff014 e59ff014 ................ 1f000010: e59ff014 e59ff014 e59ff014 e59ff014 ................ 1f000020: 40380180 403801e0 40380240 403802a0 ..8@..8@@.8@..8@ 1f000030: 40380300 40380360 403803c0 deadbeef ..8@`.8@..8@.... 1f000040: 1e8e000c 19c04000 19c08000 19c09000 .....@.......... 1f000050: 40380000 40380000 4038c064 40394000 ..8@..8@d.8@.@9@ 1f000060: e10f0000 e3c000df e3800013 e129f000 ..............). 1f000070: eb000033 e24f007c e51f1030 e1500001 3...|.O.0.....P. 1f000080: 0a000007 e51f2038 e51f3038 e0432002 ....8 ..80... C. 1f000090: e0802002 e8b007f8 e8a107f8 e1500002 . ............P. 1f0000a0: dafffffb e51f005c e2400601 e2400c01 ....?.....@...@. 1f0000b0: e240d00c e51f0064 e2800004 e51f1068 ..@.d.......h... 1f0000c0: e3a02000 e5802000 e2800004 e1500001 . ... ........P. 1f0000d0: 1afffffb e51f008c e3a01101 e1500001 ..............P. 1f0000e0: 0a000009 e24f20ec e24f30b8 e0432002 ..... O..0O.. C. 1f0000f0: e2822004 e3a03101 e0822003 e4904004 . ...1... ...@.. 5VT1310-EVB#
cfid0のデータが読めるようになったのでdtsにpartitionのregを設定して、rootfsを マウントできるようにしたところ、マウント後に固まるようになった。これは割り込み ルーチンがダミーのままでちゃんと作ってなったためだったので、コードを書いてみた。 RT1310の割り込みは32本しか無く、かなり簡単なコードですんだ。
Flashが見えるようになったのでどうにかrootfsのマウントまで進んだが、rc.d/MAINの md0をnewfsしているところで固まる。なんでだろう。。。
newfsで固まるのは、時間の処理が正常に動いていないためselectがだんまりになって いるためのようだった。 armv4系ではtimerルーチンはそれぞれ実装になっていてRT1310も作ってみたが、それが ちゃんと動いていないのかと思ったが、作った当時はそれなりに動いている事は確認 していたと思う。 よくよく確認してみるとdateコマンドで時間が全く変化しない。 sysctlのkern.cp_timeやcp_timesも変化しない。kern.timecounterのcounterは インクリメントしているしeventtimerの割り込みもデバッグライトで確認できている のだが。。。 で調べてみたところ全く動いていないわけではなくタイマー側は 動いているがカーネルさんがちゃんとしてくれていないような感じだった。 前に調べていた 時間の処理をもう一度調べなおして、ソースを確認したところ DEVICE_POLLINGというオプションがありmips系は設定されていなかったのだが、 ZRouterのarmなixpでは設定されていて、それからコピーしたRT1310でも設定されて いたので外してみたところ、dateは時間は進むようになった。ところが時間の進み方が 10秒単位だったりする。。。
とりあえずこの状態で、シングルユーザからマルチユーザに移ると、ときどきnewfsが 成功して、loginプロンプトが出るが今度はInvalid argumentでログインできない。 シングルユーザでsttyでttyu0のボーレートを設定すると同じようにエラーになる。 FDTの値が正しくなくて起動時はエラーで設定されずU-Bootのボーレートがそのまま んになるので、使えていたみたいだ。^ ^;
UARTのコードはクロックとボーレートから分周比を出して、それが範囲外だったり 分周比からボーレートを出して元のボーレートとのずれが大きい場合にEINVALを返す。
UARTのクロックはいろいろ試して判明しました。
時間が10秒毎に進む件を調べていてSCHED_ULEからSCHED_4BSDに変更したら、まったく 時間が進まなくなりました。^ ^; newfsはなぜか起動後10分くらいしてから マルチユーザに移行するとうまくいったりする。random: unblocking device.が 出た後だとほぼうまくいく。謎だ。
タイマーの使い方がまずくてeventtimerの割り込みが上がっていないのが原因だった。 割り込みがなくても時間はちゃんと進む事にびっくり。
Etherのドライバは二日くらいでどうにか動くようになった。AR2315と同じTulip系 だったが、AR2315ではMACアドレスをCSRで設定していたが、こちらはsetup frame での設定だったのでdev/dcのコードを参考に追加してみた。あと違いはCSRが8バイト バウンダリだったのとエンディアンが違ったくらいだった。
このボードにはIP175CがのっていてEthernetSwitchにコードがあったので、試して みたがmiiのコードを用意して、mdio周りをいろいろいじってどうにか認識できたが ときどき固まるので、外してしまった。miiのアクセスがCSRのビット操作で EthernetSwitchのポーリングと相性が良くないのかもしれない。 Switchのポートの状態の監視のためのポーリングだと思うが無くても問題はないので 外せれば良いのだが。
レビュー出しました。 ターゲットがあまりないので採用されるかはわからないですが。
レビューはす進んでいないが、どきどきビルドを通して焼いているが、特に問題は 起きていない。(2017/02/22)
upgradeコマンドでcfiがアップデートできない。コマンドは終了するのだが、実際には 書き込まれていない。何故かな。。。(2017/02/22)