BCM/mipsのBCM4712などの古いターゲットではFreeBSDで動くようにならなさそうで、捨てるのも忍びないのでBare Metalで使えないか試してみる事にしました。
CFEとはBroadcomがmips SOC用に提供しているブートローダーで、Common Firmware Environmentが正式名称です。Broadcomのmips SOCはSiByteを2000年に買収して始まりCFEはSiByte時代からのもののようです。SiByteはハイエンドのmips SOCを作っていたようなのですが、Broadcomになってからのmips SOCは小規模なものに変わっています。またCFEはもともとx86やppcなどのサポートなどもある大規模なブートローダーでした。
以下のページを参考にしました。
いつものように環境はFreeBSDですが、gccはpkgでgcc-mipsを入れました。いつものようにLinuxのtoolchainを使わなかったのはBroadcomが配っていたtoolchainはgcc-3だったからです。
% mips-unknown-freebsd11.2-gcc -v
Using built-in specs.
COLLECT_GCC=mips-unknown-freebsd11.2-gcc
COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/mips-unknown-freebsd11.2/6.4.0/lto-wr
apper
Target: mips-unknown-freebsd11.2
Configured with: /wrkdirs/usr/ports/devel/mips-gcc/work/gcc-6.4.0/configure --ta
rget=mips-unknown-freebsd11.2 --disable-nls --enable-languages=c,c++ --enable-gn
u-indirect-function --without-headers --with-gmp=/usr/local --with-pkgversion='F
reeBSD Ports Collection for mips' --with-system-zlib --with-gxx-include-dir=/usr
/include/c++/v1/ --with-sysroot=/ --with-as=/usr/local/bin/mips-unknown-freebsd1
1.2-as --with-ld=/usr/local/bin/mips-unknown-freebsd11.2-ld --enable-initfini-ar
ray --prefix=/usr/local --localstatedir=/var --mandir=/usr/local/man --infodir=/
usr/local/info/ --build=x86_64-unknown-freebsd11.2
Thread model: posix
gcc version 6.4.0 (FreeBSD Ports Collection for mips)
Entry.Sはそのままです。
Main.cは以下のように修正しました。
void putchar(char c)
{
volatile char* lsr = (volatile char*)0xb8000305; // Line status register.
volatile char* thr = (volatile char*)0xb8000300; // Transmitter holding register.
while(((*lsr) & 0x20) == 0) ; // Wait until THR is empty.
*thr = c;
}
void printk(const char* s)
{
while(*s)
{
putchar(*s);
s++;
}
}
int main(void)
{
printk("Hello world!¥n");
return 0;
}
シリアルのアドレスはLinuxのブートログなどで確認できます。LSRのアドレスは5か倍数(0x0aか0x14)になりますが、このチップは5でした。
ldscriptはFreeBSDのsys/conf/ldscript.mips.cfeを使います。修正点は_startをstartにしてKERNLOADADDRを0x80001000にしました。
Makefileは以下のようにしました。
CROSS=mips-unknown-freebsd11.2
Kernel.elf : Entry.o Main.o
$(CROSS)-ld -EL -T ldscript.mips.cfe -o Kernel.elf Entry.o Main.o
Entry.o : Entry.S
$(CROSS)-as -EL -msoft-float -march=mips32 -o Entry.o Entry.S
Main.o : Main.c
$(CROSS)-gcc -EL -o Main.o -c Main.c -Wall -Wextra -Werror ¥
-march=mips32 -mno-abicalls -nostdlib -fno-builtin ¥
-nostartfiles -nodefaultlibs -O2
clean:
rm Kernel.elf Entry.o Main.o
ちょっとはまったのはpkgのgccがなぜかデフォルトがBigEndianでビルドされていたことと、元のldscriptではうまく動かなかった事です。
% file Kernel.elf
Kernel.elf: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (FreeBSD), statica
lly linked, interpreter *empty*, not stripped
% readelf -h Kernel.elf
ELF Header:
Magic: 7f 45 4c 46 01 01 01 09 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: FreeBSD
ABI Version: 0
Type: EXEC (Executable file)
Machine: MIPS R3000 Big-Endian only
Version: 0x1
Entry point address: 0x80001000
Start of program headers: 52 (bytes into file)
Start of section headers: 5028 (bytes into file)
Flags: 0x50001001, mips32, o32, noreorder
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 7
Size of section headers: 40 (bytes)
Number of section headers: 12
Section header string table index: 11
ターゲットにシリアル接続して、電源投入後に^Cでブートローダーを止めて、ネットワーク接続して実行してみます。サーバ側でtftpdを使えるようにして、指定したディレクトリにKernel.elfをコピーしておきます。
CFE> ifconfig eth0 -addr=10.10.10.200
CFE> boot -elf 10.10.10.3:Kernel.elf
Loader:elf Filesys:tftp Dev:eth0 File:10.10.10.3:Kernel.elf Options:(null)
Loading: 0x80001000/256 0x80003100/4096 Entry at 0x80001000
Closing network.
et0: link down (interface down)
Starting program at 0x80001000
Hello world!
できました。
CFEはAPIがあり、もしAPIを使いたい場合には、メモリに張り付いているCFEの領域を壊してしまわないようにします。APIを使ってシリアル出力をすることも可能です。
Total memory used by CFE: 0x80400000 - 0x804A39B0 (670128)
Initialized Data: 0x80439670 - 0x8043C210 (11168)
BSS Area: 0x8043C210 - 0x8043D9B0 (6048)
Local Heap: 0x8043D9B0 - 0x804A19B0 (409600)
Stack Area: 0x804A19B0 - 0x804A39B0 (8192)
Text (code) segment: 0x80400000 - 0x80439670 (235120)
Boot area (physical): 0x004A4000 - 0x004E4000
Relocation Factor: I:00000000 - D:00000000
CFE> show memory
Range Start Range End Range Size Description
------------ ------------ -------------- --------------------
000000000000-0000003FFFFF (000000400000) DRAM (available)
0000004A4000-000000FFFFFF (000000B5C000) DRAM (available)
*** command status = 0
MMUを使う場合は、この領域を別扱いにすれば良いのかもしれませんが、BareMetalで連続したメモリー空間で使う場合には、この領域を保護するのはちょっと面倒です。アプリケーション起動直後の壊す可能性の低いうちにAPIを叩いてその後は壊しても良いようにAPIを叩かないって方法がいいのかもしれません。