Opie ROM for A300 ... への道のり、予告篇


ROM コア制作記。... 予告だけ。いや、実作業の頭のあたりは忘れ去ってるし、最後のほう終わってないし。

構成

現在、RAM は 48MB (実メモリ) + 16MB (ramdisk) 相当。SD なしでもなんとか動くように変更。 ... してるつもりだがドハマリしてるともいう。いや、単に果てしなく気力を吸い取ってくれる作業が続くだけだけど。

これ書いてるのが May 23 なのにファイル名が 20030502 になってるあたりが、ハマってる程度を表している ....

Busybox

0.60 移行。

Ramdisk の JFFS2 移行

デフォルトでは /dev/mtdblock1 の消去単位が小さすぎる。 このままだと JFFS2 にできないので、64kB にしておく。

... んだが、どこいじったか忘れた。

Ramdisk (/dev/mtd*1) を jffs2 とするには jffs2 イメージを作っておいて

# erase /dev/mtd1
# dd if=/root/.ram_default.jffs2 of=/dev/mtd1 bs=64k
# echo dd mount -t jffs2 /dev/mtdblock1
# mount -t jffs2 /dev/mtdblock1 /mnt/ram
とする。実際には pad 付きイメージを作っておいて:
# /bin/zcat /root/.ram_default.jffs2.gz > /dev/mtd1
# echo dd mount -t jffs2 /dev/mtdblock1
# mount -t jffs2 /dev/mtdblock1 /mnt/ram
として微妙に ROM をケチったが、意識してやったわけではなく script 書いた時は erase 知らなかっただけ。 cramfs は 16MB のファイルを置けない (ファイルサイズ上限は 2^24 - 1 バイト) ので、 圧縮して見た目のファイルサイズを減らしている。cramfs に入れると自動的に gzip で圧縮するようなもんなので、 事前に圧縮しても cramfs に圧縮させても量的にはあんまりかわらない .. こともなく、これくらいデカいと 数百キロバイト単位で違ってくるが、誤差っちゃー誤差だろう。

jffs2 イメージをつくるほうは:

# mkfs.jffs2 --eraseblock=0x10000 -r ramdisk_dir --pad=0x1000000 -o .ram_default.jffs2
とすると ramdisk_dir/ 以下が .ram_default.jffs2 にまとめられる。 --pad=0x1000000 で 16MB まで 0xff で埋めることを指示して、 実機上で erase (jffs2 領域を 0xff で埋めて初期化する) を省略している。

SD の JFFS2 化
メモリ以外のデバイスを jffs2 化するには blkmtd を使うが、 A300 の標準のカーネルソースでは CONFIG_MTD_BLKMTD=y すると make すら通らない。 みてみれば blkmtd のソース履歴に AC シリーズと RMK シリーズの戦いの痕跡がうかがえる。

カーネル 2.4.13 の時代、AC シリーズでは I/O 周りの高速化として alloc_kiovec() に変わって alloc_kiovec_sz() が導入された。 これによって blkmtd は v1.3 + sz パッチになっている。 これに対し、RMK シリーズでは blkmtd としてノーマルの v1.6 を使っている(使おうとしている)。 カーネルソースの大部分で sz 化されているにもかかわらず、blkmtd は 非 sz のままだ。

AC パッチを当てた直後に drivers/mtd/devices/blkmtd.c をバックアップして RMK パッチが当たるのを回避、sz コードで統一してから

# modprobe blkmtd device=/dev/hda
# mount -t jffs2 /dev/mtdblock2 /mnt/card
などとすることで SD を直接 JFFS2 で見ることができるが ...

"/dev/hda" そのものを jffs2 化しなければならない。 ── "/dev/hda2" だけを jffs2 にするということができない。
VFAT partition をなくしてしまうわけにはいかないので断念。

うぅ、つまらんオチだ。

カーネル領域の縮小と initrd 領域の拡大

カーネルは ROM 中で 1MB を占める。これを 896kB に縮め、initrd を拡大する。

カーネル〜 initrd の境界は好きな位置に置けるが、カーネル、initrd を個別に ROM に書き込めるようにするには 128kB バウンダリでなければならない。 作成したカーネルはぎりぎりで 768kB を下回ったが、ふとしたきっかけで 768kB を上回った時に initrd ごと書かなければならないことになるのが煩いので、余裕をみて 896kB に。

書き換えるのは一ヶ所だけ:

*** drivers/mtd/maps/discovery-flash.c.org       Mon Jun 17 10:22:57 2002
--- drivers/mtd/maps/discovery-flash.c.896       Wed Apr 30 20:00:28 2003
***************
*** 94,101 ****
  static struct mtd_partition discovery_partitions[1] = {
        {
                name:           "Filesystem",
!               size:           0x00e60000,
!               offset:         0x00160000
        }
  };
  
--- 94,101 ----
  static struct mtd_partition discovery_partitions[1] = {
        {
                name:           "Filesystem",
!               size:           0x00e80000,
!               offset:         0x00140000
        }
  };

むろん ROM に書き込むときは updater.pro の内容に注意する。

ROM Stamp

ROM イメージは checksum をもつ。 ふつーにカーネルなり initrd なりだけを書き換えると checksum が狂い、sysninfo で version number が出なくなる。 ま、影響っちゃそれだけだけど。

カーネルソースの arch/arm/mach-cotulla/discovery_deviceinfo.c で checksum の計算が行われているので、 これをそのまんまもってきて romstamp なるモノをでっちあげた。

ROM イメージを作ったあとで version number の刻印と、それに合わせて checksum を調整する。

まず ver 1.4 の ROM からカーネル、initrd 以外の領域を抽出:

# dd if=all.nb0 of=BIOS.bin bs=64k count=6
# dd if=all.nb0 of=PARAM.bin bs=64k count=2 skip=252
カーネル、initrd を ROM 中に占めるサイズまで padding:
# dd if=zImage of=KERNEL.bin bs=896k conv=sync
# dd if=initrd.bin of=INITRD.bin bs=14848k conv=sync
もちろん、カーネル領域を 896kB 化したケースでである。1MB 取る普通のケースでは:
# dd if=zImage of=KERNEL.bin bs=1M conv=sync
# dd if=initrd.bin of=INITRD.bin bs=14720k conv=sync
とする。 繋げて ROM image 作成し、version を刻印する。
# cat BIOS.bin KERNEL.bin INITRD.bin PARAM.bin > ROMIMAGE
# romstamp ROMIMAGE 1.41

配付目的で all.nb0 な ROM image を作成するならばともかく、 実際には不変の BIOS.bin の領域まで毎回毎回 ROM に書き込む必要はない (version 刻印は PARAM.bin の領域になされるので、checksum を直したければこれは書く必要がある) 訳だが、 書き換えしすぎで壊れる時は KERNEL.bin か INITRD.bin の領域が先なのは明らかなので気にせず ROMIMAGE 全体を書き込んでいる。

Checksum のバグ
その ROM の checksum だが、実は version number だけを書き換えても checksum は変わらない。 checksum の計算に参加しているのが word alignment したときの最下位バイトだけで、 version number は 3 〜 4 バイト目に含まれているからだ。

CRC 算出ループ:

unsigned int  *pROMAddress = (unsigned int*)0xe8020000;
for( i = 0 ; i < uiLen ; i+=sizeof(unsigned int) ) {
   crc = (((crc >> 8) & 0x00FFFFFF) ^ crc32table[(crc ^ *pROMAddress) & 0x000000FF]);
   pROMAddress += 1;
}
*pROMAddress の最下位 1 バイトしか crc32table のルックアップに参加してねーよ ...。 ま、壊れる時はバーストで壊れるだろうから、これでいいんかね。

マシンタイムとシステムタイム

suspend/resume したりすると 9 時間ずれる。 /etc/localtime/usr/share/../Tokyo だかなんだかへ symlink するのがふつーのやりかただが、 これだけだと狂うアプリがある。どーやら /etc/localtime みてないやつがいる。

結局、マシンタイム(RTC 内で保持する時刻) を UTC とし、/etc/localtime を省略した。 /etc/adjtime が LOCAL でなく UTC になっていることを確認のこと。 TZ さえセットしておけばシステムタイム (/bin/date などが返す値) のほうは正しく日本のものをさす。

これって qtopia 1.5.0j のほうのメールの時刻が狂うってのもこの件じゃないかと思わないでもないこともないでもないが未確認。

LC font の font metrics

LC font にかぎらず、libqte 内の bdf to qpf のコードが果てしなくバグってくださってるおかげで qpf の font metrics はボロボロになっている。それに合わせようと各アプリが七転八倒した結果、 bdf to qpf で metrics が期待するものになるよう意識的に誤った metrics の bdf を突っ込んで得た qpf フォントでは表示が狂う。

てのはつまり、ascent/descent の解釈のことだが。Qt では font height = ascent + descent + 1 になっていて、baseline が幅をもつ。bdf では height = ascent + descent で baseline の幅はゼロ。 にもかかわらず、bdf to qpf で ascent, descent はそのまま qpf にコピーされるため、結果として font height が 1 増える。 これが LC font の本来のグリフにもかかわらず、embedded konsole で罫線の上下が繋がらない理由だ。

Underline の置き場所も、baseline 直下におかれるが、LC font では descent がゼロである結果、 Underline がフォントの外側に置かれる。 で、自前でフォントを draw しているアプリの一部で underline の消し忘れが発生する (これは embedded konsole で underline が残るバグとはあまり関係がない。こちらは別要因)。

そしてもうひとつ。枠無し embedded konsole で LC font を使うと、左端が表示外の背景黒に融けこんで読めん。

このあたり、LC font metrics と embedded konsole の metrics 処理を書き直すはめになった。

Rotate orientation と ttf 化

Rotation して landscape に使うと、qpf では t5 や t10 のグリフを必要とする。 一方、ttf では一つあれば t0, t5, t10, t15 ぜんぶ共通である。

ttf のほうがフォントファイル自体は膨れるが、rotation こみにすると ttf でもっておいたほうがオトクだ。 むろん、速度的に遜色ないことは確かめておいた。 pfb などもふつーに使えたので、ttf より pfb を使うことが多いが (ファイルサイズが小さいもんで)。

... が、bdf to ttf で embedded glyph だけの日本語フォントを作る方法がないだよ。

念のためだが。lcfont10.bdf など一つの bdf を embedded glyph としただけの ttf は作れる。 ここで問題にしてるのは、lcfont10.bdf, lcfont12.bdf, lcfont16.bdf をすべて embedded glyph として含んだ ttf を作る方法がない ── という話。 一つのフォント内でサイズ違いを含んでおかないと、フォントサイズ変更にアプリが耐えられない。

transformations と freetype

さて、その freetype. qpdf で縦書きサポートをまじめにやろうとすると、Qt のほうで transformations のサポートが欲しい。

で。この transformations と freetype が arm では喧嘩してくださって両立しない。なんでやねん。

余談だが。qconfig.h 中でのこれらの機能をあらわすフラグは QT_NO_TRANSFORMATIONS などと書かれているわけだが、 それらのフラグに機能を代表させて 「QT_NO_TRANSFORMATIONSQT_NO_FREETYPE が喧嘩する」などとは表記しづらい。 TRANSFORMATIONS がないことと FREETYPE がないことが喧嘩してるわけではないからだ ... このあたり、みんな表記の仕方に苦労してるよーである。だいたい、会話するにも不便なんだがな。

qpdf

実は内部的に CID をサポートし、日本語縦書き横書き混在の pdf すら読める。
実機で使おうとするとディスク足らんけど ...

ちなみに QT_TRANSFORMATIONS 入れると (← 開きなおった表現) PDF 内の rotate matrix が扱えるようになって斜体とか変形が表現できるようになるが、 座標系の扱いがバグってるらしく上下ひっくりかえるのと、mask が未実装なので色塗りがはみ出たりして笑える。

embedded-konsole

枠無し + 改良型 LC font で使えているが、枠なしにした結果、ボタンを置く場所がなくて multiple console になってない。あぅ。

今は日本語変換ラインも使ってない(opie では使えない) ので、ボタン置く余地はあんだけど。

sysinfo と /proc/mounts

bind mount を多用すると sysinfo のディスク残量表示んとこが溢れる。 同じデバイスを何回も表示するのは無駄なんで書き換えたんだが ...

mount での /proc/mounts の扱いが busybox と本物の gnu mount で違う。 busybox mount を仮定する sysinfo だと本物の gnu mount 使った時に表示が変。

システムの ROM 化

うぅ、linux の ROM 化のためのテキストが欲しいぞ〜。 FS の ROM 化ツール、もってたんだけど思い出せん上に検索もでけん (いや、よー発音でけん spelling だったもんで覚え辛くって)。

Symlink の扱い

絶対パスへの link になっているため、母艦上で cp とかが自然にできない。

とくに ls が使えない ... デフォルトでは symlink を判定せず、その symlink 先が正しい先を指していないため、 ls -l するなりして symlink かどうかを目で確かめ、そのうえで手で symlink を辿らなければならない。

chroot で / を設定しなおすことができない: PATH, LIBRARY_PATH を /bin, /lib 等から外すことはできても、 /lib/ld-linux.so.2 が正しくなければならないという制約は厳しい。

/lib/modules
  1. /rom 下に chroot した時に正しく動くためには、/rom の中から /rom の上への symlink があってはならない。
  2. /lib/modules/ 内はディレクトリの symlink が禁止されている。 ... いや、分かるけど、あまりこういう undocumented な仕様は止めていただきたいと思うのだが。 この場合、たいした意味ねーし。
/lib/modules と /rom/lib/modules の関係は、1. から /rom/lib/modules が実体でなければならないが、 2. からは /lib/modules が実体でなければならない。

同様に /lib/ld-liunux.so.2 も symlink であってはならない ... だと思ったが、 今回はいちいちテストしなかったので本当かどうかは不明。

母艦上の ${SUBSYSTEM}/rom に chroot してテスト、${SUBSYSTEM}/ をそのまま ROM 化して実機へ ── という方針がここで破綻する。


[日記へ] [目次へ]