[ARM] compressed kernel

2013/10/20linux::ARMimport

zImageの展開処理

調査対象は、【kernel 3.0.39 "Sneaky Weasel"】です。

例として、u-bootから起動する場合を考えます。
ここで上げるのは、とあるボードへの移植時に調査した値です。
ボード依存部の値は、デタラメに書いている可能性があるので、注意してください。


NOR bootの場合は、ROM上のコード実行が可能ですが、昨今の方式*1ではbootloaderがDRAM上に展開してから実行することが多いでしょう。

u-bootが読み込むファイル、uImageは、zImageにuImageヘッダがついたもの。
図にしてみると以下のイメージ。(AAでスミマセン)
+------------------------+
| u-boot header(64oct.)  |
+------------------------+
| zImage decompress code |
| .text                  |
| (with compressed data )|
| .got                   |
| .got.plt               |
| .bss                   |
+------------------------+
このデータの読み込みは、u-bootによって行われるため、任意のアドレスに展開されます。


ldscriptを見ると、ざっくりとこんな感じ。
  _text = .;
  .text : {}
  _etext = .;
  _got_start = .;
  .got			: { *(.got) }
  _got_end = .;
  .got.plt		: { *(.got.plt) }
  _edata = .;

  __bss_start = .;
  .bss			: { *(.bss) }
  _end = .;

*1 : NAND/SDから2nd boot programを読み出して、本当のローダを呼び出す。

事前準備

実際には、コードを読みながら参照していった結果です。

FILE: arch/arm/boot/compressed/Makefile
# Supply ZRELADDR to the decompressor via a linker symbol.
ifneq ($(CONFIG_AUTO_ZRELADDR),y)
LDFLAGS_vmlinux += --defsym zreladdr=$(ZRELADDR)
ZRELADDRは、以下で定義されます。
FILE: arch/arm/boot/Makefile
ZRELADDR    := $(zreladdr-y)
右辺は、SoC依存部にて定義されています。
FILE: arch/arm/mach-*/Makefile.boot
zreladdr-y	:= 0x40008000
params_phys-y	:= 0x40000100
initrd_phys-y	:= 0x40800000


zImage先頭から

エントリーポイントはここ。

bootloaderから飛んでくるところ

アドレスは任意。

FILE: ./boot/compressed/head.S
start:
 ※cache有効, TLBはVA-PA一対一対応, 

restart:
		.align	2
		.type	LC0, #object
LC0:
		.word	LC0					@ r1
		.word	__bss_start			@ r2
		.word	_end				@ r3
		.word	_edata				@ r6
		.word	input_data_end - 4	@ r10 (inflated size location)
		.word	_got_start			@ r11
		.word	_got_end			@ ip(r12)

		.word	.L_user_stack_end	@ sp

		.size	LC0, . - LC0

		/*
		 * We might be running at a different address.  We need
		 * to fix up various pointers.
		 */
		sub	r0, r0, r1		@ calculate the delta offset
		add	r6, r6, r0		@ _edata
		add	r10, r10, r0	@ inflated kernel size location
LC0(DRAMの位置)から、"LC0"の配置した位置(ldscriptでは先頭0、コード配置後のLC0の位置)を差っ引いて、r0に保持する。
圧縮カーネルの、展開後のサイズは、build systemによって、圧縮カーネルの後ろにLEで付与される。したがって、r10へloadする値は、input_data_endから4引いてある。(32bit決め打ちだね)
spも、relocate後のアドレスをセット。r10にsp+64kのアドレスをセット(malloc用に64k確保)(CONFIG_ZBOOT_ROM未定義の場合。定義時はコード本体がROMに入るから、_edataの後ろに配置する)

このイメージがロードされる位置がどこでも動けるようになっている。

展開後のkernel位置と、この展開コード、edata類の空間が重なっているかをチェックする。重複している場合、reset〜edataまでを、後ろからコピーする(memmove相当)。その後、コピーした後のrestartへjumpする。(これで重複しない位置で再度relocateする)

※ユーザモードで入ってきた場合、SVCで特権を得るようになってます。
ヴェクタ位置へのロードを期待しているようですが、必ずしもそうではないから、ちょっと不味そうな気がするネ。

relocate後

decompress処理のソフトと、圧縮イメージとを再配置して、kernel imageを展開できる状態になって、wont_overwriteへたどり着く。前述のように、最初のバイナリがロードされる位置によって、ここへ直接来ることもあれば、relocateすることもありますね。
wont_overwrite:
/*
 * If delta is zero, we are running at the address we were linked at.
 *   r0  = delta
 *   r2  = BSS start
 *   r3  = BSS end
 *   r4  = kernel execution address
 *   r7  = architecture ID
 *   r8  = atags pointer
 *   r11 = GOT start
 *   r12 = GOT end
 *   sp  = stack pointer
 */
・GOT書き換え(オフセット分加算して、飛び先がrelocate先のアドレスになるように)
・BSSをゼロクリア


本来のImage(vmlinux)へ

kernelの展開先アドレスを第一引数、spからsp+64kを第二〜三引数に渡して、第四引数にアーキテクチャIDをのせてcall。
void decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p, unsigned long free_mem_ptr_end_p, int arch_id)
展開が終わったら、cache clean、cache-offしてから、圧縮前のkernel先頭へ飛ぶ。


ARM boot

2013/10/19linux::ARMimport
kernel bootからの処理をみてみましょう。
kernel先頭へは、u-bootやbare-boxから飛んできます。

そこはそれ。wiki contentsとして記事を置いておきます。

LInux開発入門

2013/06/09linux::入門編import
冒頭から逃げを書いておきますが・・・
手探り状態、読者対象が具体的に見えない状況下で記載を始めます。
昨今、マトモな出版物も増えてきているので、需要がないかもしれませんが、少しでも道標になれば幸いです。

Linux OSを用いた開発

以前までは、組み込みといえばuITRON系のRTOSが幅をきかせていましたが、
プロセッサ能力向上・メモリ単価の減少、要求要件の拡大などの要因により、
見直されてきているように思います。
最も影響があるのはNetwork Protocol Stackや、複数プラットフォームで共通的に使えるソフトでしょう。

もちろん、ITRON系のリソースも現役です。少ないROM/RAM、軽量なマイコンでも動くので、住み分けができるでしょう。
より低機能であればOSレスもありますし、畑は広大です。

さておき、ひとことでLinux OS開発といっても、OSはもよとり、コンパイラ・アセンブラ・リンカその他ツール類すべてがOpen Source Softwareを使用します。自らソースコードを元に開発環境や実行環境を用意することができます。MicrosoftのVisual Cのように、コンパイラに対価を支払うのとは対照的ですが、何か問題が起きたとき、起きそうなとき、それを知るための方法を、どのように確保するのか、が課題となります。これを商売にしている会社もありますね。

構成

以下に、クロス開発環境を前提としたソフトの構成例を記します。
開発ツールを買えば、裏ではこれらのソフトを準備してくれたりします。
MSDOSやWindows、商用UNIXでやってきた方には、高い金でコンパイラや統合開発環境を買った記憶があることでしょう。

Linux_SW_001.png


黄色い部分が、ターゲットに載せるべきものです。
右下にある、libraryを除く開発ツールは、必須ではありません。必要に応じてセルフ開発環境やデバッグツールを取り込むのがよいでしょう。

ubuntoやdebianの各arch向けディストリを使う場合は、セルフ環境も必要になりそうな気はします。いや、基本はバイナリインストールになっているしょうか。

ベンダ提供

あえてドライバ・アプリのところへ、ベンダ提供〜を記載しました。
実務で使っていると、必ずしもすべてをopenにしているのでは無いですから、ね。各社の製造ソフトに関しても、GPL伝播していないものは公開義務を負いませんから公開されることは無いです。GPLv2までの場合は、その再配布責任を追うことから、なんらかの手段で製品で使用したtarballなどの提供を受けることができます。
液晶テレビ・レコーダ・STB・そのほかそれっぽい家電の説明書を眺めてみれば、Open Source使用の旨と、その製品で使用したものの入手方法が記載されているでしょう。
尤も、オンラインでさくっと手に入るところもあれば、電話問い合わせ・郵送による配送もあります。
エンドユーザから公開の依頼が頻繁にはないのでしょう、かねぇ?

ライセンスの話が出てきましたが、これだけで深いネタになります。
僕も把握しきれておらず、法的な問題になる場合は判例を調べた上で判断したほうが良いでしょう。
個人で楽しむ分にも、価値が認められ、公開義務を追うような作りをした場合は、開示を迫る人がでてくるかもしれません。

toolchain

いわゆるコンパイル環境、です。ソースコードをターゲットで動くバイナリに変換します。
最近は LLVMというのが注目を集めているようです?

汎用性を考慮されたソフトの配布では、Makefileを利用していることでしょう。
また、autoconfや独自のconfigrationツールを用意して、buildするホスト環境差分を自動的に検出・適切に変更するような手段を提供しています。

しばらくしてわかったこと

何事も過去の経緯を知ることが理解への早道に繋がることがあります。同じようなものがなぜ複数存在するのか、ディストリビューションのそれぞれの目的、本当に全てが無料なのか。製品に搭載するために必要なことは?などなど。いまだにわからない事だらけですが、所詮個人がわかる範囲なんてのはしれてるわけで。

必要なとき、必要なところで、短時間でほしい情報に辿りつけるハナ・直感力を持てるようにしておくのが、対策となろうかと思います。もしくは大まかに畑を分割して、担当範囲を分けておくか、です。ですよね・・・?
後半は妄想でしかないので...