MIPSやARMなどでカーネルがメモリサイズを獲得する方法は以下のようなパターンがあります。
- SOC毎のソースでのハードコード
- FDT(dts)から
- カーネルコンフィグレーションのoptionで
- bootから起動時に引き渡された値
- bootがsocに設定した値をレジスタから拾う
この事で、先日ちょっと困った事がありました。
最近いじっているMindspeed/armでdtsに書いた値のちょうど倍の値が起動時のログに出力されるのです。
実メモリよりも大きくカーネルが認識していると、立ち上がったとしてもじきに落ちます。
これはu-bootがメモリ情報をカーネルに引き渡していて、カーネルoptionのLINUX_BOOT_ABIが指定されているとsys/arm/machdep.cのlinux_parse_boot_param()でメモリ情報を拾ってdtsの情報と二重に登録してしまうことが原因でした。
FDT_DTB_STATICでdtsを自前で持っている場合memory項目は無いと起動しません。
/* Grab physical memory regions information from device tree. */
if (fdt_get_mem_regions(mem_regions, &mem_regions_sz, &memsize) != 0)
panic("Cannot get physical memory regions");
横道にそれますが、このpanicはcninit()の前なので、メッセージを見る事ができないので、やっかいです。熟練してくるとコンソールに出なくてもメッセージが見えてきます。(嘘です)
なので、linux_parse_boot_param()のメモリを拾っているところは
#if !defined(FDT_DTB_STATIC)
case ATAG_MEM:
arm_physmem_hardware_region(walker->u.tag_mem.start,
walker->u.tag_mem.size);
break;
#endif
が良いような気がします。freebsd-armなMLにポスとしてみましたが、反応ありません。。。
このopetionや関数名のLINUXという名前は、ubootでも良い気がしますが、今さら直すまでの事もでもないですね。
多くのMIPS SOCは0x80000000からメモリがあるのですが、RT2880は0x88000000からになります。このためdtsには0からではなくオフセットの0x08000000を入れます。
memory@8000000 {
device_type = "memory";
reg = <0x08000000 0x1000000>;
};
FreeBSDのmipsなどではブートローダーのメモリコントローラーの初期化を期待していて、FreeBSD自体では初期化はしません。そのためブートローダーの認識しているメモリとFreeBSDが認識するメモリのサイズが違っているとpanicすることがあります。
dtsにmemoryを書く場合はdtsiではなくdtsに入れるべきである。これはdtsiはCPUなどのことを書いていて、ボード固有の設定はdtsにあるべきだからだ。
ブートローダーのメモリサイズの設定は、ビルド時に決め打ちや、自動認識など様々。
dtsにmemoryがあってもそのブロックに以下が書かれていないと認識しないようです。
+ #address-cells = <1>;
+ #size-cells = <1>;
上の問題はarmの人もまったく関心無いみたいです。これはarmはu-bootのセカンドブートローダーを使って、dtbをSTATICではなくセカンドブートローダーからもらうので、問題にならないためだと思います。STATICのことなどもう忘れてしまったのかもしれません。:(