2012年2月24日金曜日

C言語のswitch文で ...

Linux のコードを見ていると、こんな書き方もできるのかぁ、というものに出くわすことがありますね。

例えば、
arch/arm/mach-rpc/irq.c  にある、以下のようなコード。

                switch (irq) {
                case 0 ... 7:
                        irq_set_chip_and_handler(irq, &iomd_a_chip,
                                                 handle_level_irq);
                        set_irq_flags(irq, flags);
                        break;

                case 8 ... 15:
                        irq_set_chip_and_handler(irq, &iomd_b_chip,
                                                 handle_level_irq);
                        set_irq_flags(irq, flags);
                        break;

                case 16 ... 21:
                        irq_set_chip_and_handler(irq, &iomd_dma_chip,
                                                 handle_level_irq);
                        set_irq_flags(irq, flags);
                        break;

                case 64 ... 71:
                        irq_set_chip(irq, &iomd_fiq_chip);
                        set_irq_flags(irq, IRQF_VALID);
                        break;
                }



...」で範囲指定できるらしい。見た時びっくりしました。

 
gcc のマニュアルを参照すると
http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/C-Extensions.html#C-Extensions

これはGNU 拡張機能らしいです。
(Case Ranges: `case 1 ... 9' and such. )

gcc に -pedantic をつけると非標準機能を使っていると警告してくれるみたい。
ポータビリティを気にする場合は便利かも。

上のような書き方をすると

警告: range expressions in switch statements are non-standard

という風に warning 出してくれました。

2012年2月23日木曜日

UNK/SBZP

ARM ARM (ARM Architecture Reference Manual) で UNK/SBZP という用語が頻発していて、何の略だろうと思ったら、

UKNOWN on reads, Should-Be-Zero-or-Preserved on writes.
のことらしい。

レジスタのreserved ビットに対する、ソフトでの扱い方を述べているようです。
reserved ビットは、0読み出し、書き込み無視のようにハードではたぶん実装しているはずだが、ソフトはそれを期待するなということだと思います。

2012年2月13日月曜日

LinuxカーネルのMakefileを解析する その6

今回は、Linuxカーネルのmakeシステムの肝とも言える、drivers/, kernel/, fs/ などといったディレクトリを階層的に降りていきながら、各階層に置かれたMakefile(またはKbuild)ファイルをparseする仕組みを解明してみたいと思います。

ちなみに、各ディレクトリ階層に置く Makefileの書き方は
Documentation/kbuild/makefile.txt
に解説がありますが、ここでもちょこっと解説しておきます。

各階層のオブジェクトはMakefile の中で obj-y += hoge.o のようにobj-y変数に追加します。
また、 obj-y += piyo/ のようにディレクトリを追加すると、サブディレクトリに降りるという意味になります。
オブジェクトはディレクトリごとに built-in.o というファイルにまとめられます。

例で示した方がいいかもしれないですね。
以下のようにディレクトリ階層があって、各階層ごとに cのソースと Makefileがあったとする。


hoge
├── Makefile
├── hoge.c
└── piyo
    ├── Makefile
    ├── fuga
    │   ├── Makefile
    │   ├── fuga1.c
    │   └── fuga2.c
    └── piyo.c


Makefileにはその階層の オブジェクトと一つ下のディレクトリを記載します。

hoge/Makefileの内容:

obj-y := hoge.o  piyo/


hoge/piyo/Makefileの内容:

obj-y := piyo.o fuga/


hoge/piyo/fuga/Makefileの内容:

obj-y := fuga1.o fuga2.o


みたいに書いておく。


このとき、 各階層の.o は .c ファイルをコンパイルして作られます。

さらに、
hoge/piyo/fuga/fuga1.o と hoge/piyo/fuga/fuga2.oは ld -r でまとめられて、
hoge/piyo/fuga/build-in.o になります。

さらに、
hoge/piyo/fuga/built-in.o と hoge/piyo/piyo.o がまとめられて hoge/piyo/built-in.oになる。

さらに、
hoge/piyo/built-in.o と hoge/hoge.o がまとめられて、hoge/built-in.o になる。

hoge/built-in.o は他のオブジェクトとトップレベルでリンクされて カーネルイメージになるという具合です。


今回解読するファイルはトップのMakefileとscripts/Makefile.build, scripts/Makefile.lib あたりです。


まず、トップのMakefileの500行目付近にある

init-y    := init/
drivers-y := drivers/ sound/ firmware/
net-y     := net/
libs-y    := lib/
core-y    := usr/

に注目します。これが、これから降りていこうとするトップ階層のディレクトリ達です。

さらに700行目付近に以下のように書いてあります。


core-y        += kernel/ mm/ fs/ ipc/ security/ crypto/ block/

vmlinux-dirs    := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
             $(core-y) $(core-m) $(drivers-y) $(drivers-m) \
             $(net-y) $(net-m) $(libs-y) $(libs-m)))

vmlinux-alldirs    := $(sort $(vmlinux-dirs) $(patsubst %/,%,$(filter %/, \
             $(init-n) $(init-) \
             $(core-n) $(core-) $(drivers-n) $(drivers-) \
             $(net-n)  $(net-)  $(libs-n)    $(libs-))))

init-y        := $(patsubst %/, %/built-in.o, $(init-y))
core-y        := $(patsubst %/, %/built-in.o, $(core-y))
drivers-y    := $(patsubst %/, %/built-in.o, $(drivers-y))
net-y        := $(patsubst %/, %/built-in.o, $(net-y))
libs-y1        := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2        := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y        := $(libs-y1) $(libs-y2)

  ...

vmlinux-init := $(head-y) $(init-y)
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux-all  := $(vmlinux-init) $(vmlinux-main)
vmlinux-lds  := arch/$(SRCARCH)/kernel/vmlinux.lds


これをふまえて、

$ make vmlinux
としたときに何が起きるかを見てみます。


vmlinuxの構築ルールは900行目付近にあります。


vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
ifdef CONFIG_HEADERS_CHECK
    $(Q)$(MAKE) -f $(srctree)/Makefile headers_check
endif
ifdef CONFIG_SAMPLES
    $(Q)$(MAKE) $(build)=samples
endif
ifdef CONFIG_BUILD_DOCSRC
    $(Q)$(MAKE) $(build)=Documentation
endif
    $(call vmlinux-modpost)
    $(call if_changed_rule,vmlinux__)
    $(Q)rm -f .old_version


vmlinuxはリンカースクリプト$(vmlinux-lds)の他、$(vmlinux-init) $(vmlinux-main)といったオブジェクトに依存しています。
これらをリンクすることで vmlinuxができます。
$(vmlinux-init) $(vmlinux-main) はほとんどは、トップ階層のディレクトリに /built-in.o をつけたものです。
で、*/built-in.o はどうやって作るかというと、

# The actual objects are generated when descending,
# make sure no implicit rule kicks in
$(sort $(vmlinux-init) $(vmlinux-main)) $(vmlinux-lds): $(vmlinux-dirs) ;


の行です。$(vmlinux-dirs) というディレクトリにだけ依存しています。
$(vmlinux-dirs) の構築ルールは以下です。

PHONY += $(vmlinux-dirs)
$(vmlinux-dirs): prepare scripts
 $(Q)$(MAKE) $(build)=$@

前回説明したように、build というのは
build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj
のことでした。

ここからは、Makefile.build を呼び出して、ディレクトリを降りていく処理です。
例えば、
$ make drivers
とやると、 drivers/ ディレクトリを降りて行ってくれます。

ここからは scripts/Makefile.build の解析です。

例えば、 make -f scripts/Makefile.build obj=drivers

とした場合です。

src := $(obj)

objの値(drivers)がsrcにコピーされる。

40行目付近で、

# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)

とありまして、$(obj)/Makefile がinclude されます。

ここを抜けてきたときに、obj-y にリンク対象のオブジェクトファイルと、サブディレクトリが入っています。
その下に

include scripts/Makefile.lib

とありまして、Makefile.lib の中で obj-y を加工します。
関係ある部分だけを抜き出すと、、

__subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y += $(__subdir-y)
...
obj-y  := $(patsubst %/, %/built-in.o, $(obj-y))
...
subdir-ym := $(sort $(subdir-y) $(subdir-m))
...
# tell kbuild to descend
subdir-obj-y := $(filter %/built-in.o, $(obj-y))

...
obj-y  := $(addprefix $(obj)/,$(obj-y))
obj-m  := $(addprefix $(obj)/,$(obj-m))
lib-y  := $(addprefix $(obj)/,$(lib-y))
subdir-obj-y := $(addprefix $(obj)/,$(subdir-obj-y))


最終的に、
obj-y には同階層のオブジェクト群と一つ下の階層のbuilt-in.o
subdir-obj-y は一つ下の階層のbuilt-in.o
subdir-ym は一つ下の階層のディレクトリリスト
が入ります。


で、デフォルトターゲット __build の生成ルールを見ると以下のようになっています。

__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
  $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
  $(subdir-ym) $(always)
 @:
ルールは @: なので何もしませんので、依存関係だけを書いています。
重要なのは
__build: $(builtin-target)
の部分です。

$(builtin-target)  は $(obj)/built-in.o のことです。
$(builtin-target) を生成するルールは以下です。

$(builtin-target): $(obj-y) FORCE
 $(call if_changed,link_o_target)


$(obj-y) を全部集めて、ld -r でまとめて1個のbuilt-in.oにするということです。

一つ下の階層のbuilt-in.o を作るためのルールは以下の部分です。

# To build objects in subdirs, we need to descend into the directories
$(sort $(subdir-obj-y)): $(subdir-ym) ;
...

PHONY += $(subdir-ym)
$(subdir-ym):
 $(Q)$(MAKE) $(build)=$@


$(subdir-ym) ディレクトリ以下にさらに降りていくために、再帰的に Makefile.build を呼び出しています。
$@ には、drivers/gpic, drivers/pci のように一つ下の階層のディレクトリ名が入っています。

このように、再帰的make を使うことでディレクトリ階層を降りていきます。

LinuxカーネルのMakefileを解析する その5

LinuxカーネルのMakefileのファイル構成についてざっくり見てみる。


まずトップにある Makefileですが、いろいろなmake変数をセットアップして、exportしています。
exportされた変数は、再帰的に呼び出したmakeにも引き継がれます。
トップのMakefileが行っているトリッキーな処理については「LinuxカーネルのMakefileを解析する その1」「その2」で説明しました。
他にも、トップレベルの構築ルールも書かれています。


それ以外に、共通で使用するmakefileが scripts/ 以下にあります。

scripts/Kbuild.include には共通で使われる便利な変数や関数が入っています。
Kbuild.include は トップのMakefile や scripts/Makefile.* からincludeされています。

scripts/の下には Makefile.* という名前のファイルがあります。
Makefile.build, Makefile.clean, Makefile.fwinst, Makefile.headersinst, Makefile.help, Makefile.modbuiltin, Makefile.modinst, Makefile.modpost
といったファイルです。



これらは、再帰的にmakeを呼び出すときに使わます。
全部の構築ルールを1つのMakefileに書いてしまうと見づらいので、構築するターゲットの種類ごとにサブのMakefileに分離しているわけですね。

これらの使い方は、ほぼ決まっていて、

hoge:
    $(MAKE) -f $(srctree)/scripts/Makefile.build obj=$@

みたいな書き方をします。
hogeを構築するルールは scripts/Makefile.build のようなサブのMakefileに分離されていて、それを再帰的に呼び出します。
その時に、何を作りたいかは obj変数にセットします。

なお、よく使われるものについては、短く書けるように、

build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj

といった変数が用意されています。これを用いれば、上記のものは、

hoge:
    $(MAKE) $(build)=$@

のように非常に短く書くことができます。
この記法はあちこちで多用されています。

同様に、
clean := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.clean obj
hdr-inst := -rR -f $(srctree)/scripts/Makefile.headersinst obj
modbuiltin := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.modbuiltin obj

あたりは短く書くための便利変数が用意されている模様。


名前は似ているんですが、Makefile.host と Makefile.libは他の Makefile.* と違って、単に他のMakefileから includeされているだけです。

2012年2月12日日曜日

LinuxカーネルのMakefileを解析する その4

今回は、LinuxカーネルのMakefileで多用されている書き方について、いくつかピックアップ。



まず、FORCEから。

hoge: piyo FORCE
    ...

みたいな感じでよく使われています。


FORCE はトップのMakefileの最後の方で

PHONY += FORCE
FORCE:

# Declare the contents of the .PHONY variable as phony.  We keep that
# information in a variable so we can use it in if_changed and friends.
.PHONY: $(PHONY)


のように書いてあります。(他にも scripts/Makefile.build とかにも書いてあります。)

つまり、FORCEはPHONYターゲットです。
だから、

hoge: piyo FORCE
    ...

のようにFORCEを依存関係に連れている hogeは新しかろうが古かろうが、かならず ... の部分が実行されます。

だったら、上のは
.PHONY:  hoge
hoge: piyo
    ...

と書いても同じことじゃないの?ということになりますが、この場合はたぶん同じですね。。

おそらくなんですが、PHONYターゲットには、 
.PHONY: this_is_phony
のように使って、実在のオブジェクトなんだけれど、毎回ルールを実行してほしいときには
this_is_object: FORCE
を使っているんじゃないかと想像するんですが、

PHONY += firmware_install
firmware_install: FORCE
    @mkdir -p $(objtree)/firmware
    $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_install

のように、PHONYターゲットでありながら、 FORCEを依存関係に連れている部分もあるようです。

ただ、パターンルールと併用するときには .PHONY: は使えないです。

例えば、

%.o : %.c FORCE
    $(CC) -c -o $@ $<

.PHONY: FORCE
FORCE:


という書き方はできます(%.o は必ず再構築されます)が、


.PHONY: %.o
%.o : %.c
    $(CC) -c -o $@ $<


という書き方はできませんね。これが .PHONY::FORCE の違いといえば違いのような。




2つ目の話題は「何もしない構築ルール」について。

(例1)
$(CURDIR)/Makefile Makefile: ;

のような書き方。

同様に

(例2)
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
     $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
     $(subdir-ym) $(always)
    @:

みたいな書き方です。

たぶん、

hoge: piyo ;



hoge: piyo
    @:


はほとんど同じことです。構築ルールの部分は何もしない。
ただし、前者の場合l何もしなかった場合に、
「`hoge'に対して行うことはありません」といちいちmakeがメッセージを出して来るので、それが嫌な場合は後者の @:の方を使います。
ちなみに、「:」はシェルのビルトインコマンドで、「何もしないコマンド」です。
ただし、make がエコーを返すので、 @ をつけてエコーを抑制しています。

あえて、こういった書き方をする理由は2通りあって、一つはmakeのビルトインルールを上書きするため。(例1の場合)

もうひとつは、他の場所にはもう構築ルールはなくて、ほんとに何もしなくていいんだよ、とはっきりさせること(例2の場合)

例えば、単に

hoge: piyo

のように依存関係だけを書いてしまうと、ずっと離れた場所に


hoge: fuga
    ...

のようなhogeを構築するルールが記述されているかもしれない。
上記の2つはまとめられて、

hoge: piyo fuga
    ...

と同じことなのだが、1つのターゲットに対し依存関係は何行でも書くことができてしまう。
一方、ターゲット1つに対し、構築ルールは1つしか存在できない。

だから、
hoge: piyo
    @:

と明示的に「何もしない」というルールを書いておけば、他にもうルールはないことがはっきりする。
もしも遠く離れたところに、

hoge: fuga
    ...

みたいな記述が現れたしても、hogeを構築するルールが「@:」と「...」の2つもあるのはおかしいよ、ってmakeがwarningで教えてくれるからだ。

ちなみに、makeには1つのターゲットに対し、複数の構築ルールを持たせることのできる、「二重コロンルール」というのも存在するのだが、マニアックなので脱線するのはやめておこう。

LinuxカーネルのMakefileを解析する その3

とりあえず、Linuxカーネルを離れて、makeの一般論から。

ある程度大きな規模のビルドシステムを作ろうとしたときに、設計思想としては、再帰的makeを採用するか、非再帰的makeを採用するかに分かれると思います。


(1)再帰的make
では、上位階層のMakefile は下位階層のMakefileを再帰的に呼び出し、さらにその下にディレクトリがあればさらに呼び出し、といった具合に、1つ下のMakefileを呼び出して行きます。


top
├── Makefile
├── hoge
│   └── Makefile
└── piyo
    └── Makefile

といった感じの階層があったとします。
(余談ですが、上のようなディレクトリ構造の絵を書くには、"tree"コマンドが重宝します。)


再帰的makeでは、top/Makefile の書き方は例えば次のような感じになっているでしょう。


.PHONY: all hoge piyo
all : hoge piyo
    ...

hoge piyo:
    $(MAKE) -C $@



hoge をmakeするには、 make -C hoge のようにhogeディレクトリへ移動して、makeを実行します。
$(MAKE) -C $@ は (cd $@; $(MAKE))と書いても同じことです。



(2)非再帰的make
では、

top/Makefile は


include hoge/Makefile piyo/Makefile

all:
    ...



みたいに、下位ディレクトリのMakefileをincludeするだけで、再帰的にmakeを呼び出すことはないです。
 (hoge, piyo)/Makefile には各ディレクトリの構築すべきオブジェクトファイルのリストなどが書かれているだけです。


さて、LinuxカーネルのMakefileですが、分類としては(1)再帰的makeになります。
ただ、大きく違うのは再帰的にmakeを実行するときに、 -C オプションを使いません。
(前2回の解説で、-O オプションを処理する時とかに make -C を使ってたりしますが、ここは例外です。)

LinuxカーネルのMakefileは -f オプションを使って、再帰的にmakeを呼び出します。
上の例と比較しますと

hoge piyo:
    $(MAKE) -f $@/Makefile
といった感じの呼び出しになります。

-C を使うのと、-f を使うのでは何が違うのかといいますと、、

-C hoge はcurrent directory の移動が起きます。
なので、hoge/Makefileを実行しているときは、top/hoge にいます。

一方、-f hoge/Makefile で呼び出した時は、最初から最後までtopディレクトリにいます。

include文や、オブジェクトの構築は、相対パスで記述する限りは、current working directory が起点になるので、今どこのディレクトリにいるのかを理解しておくことは大事です。

Linuxは(-Oを処理するなどの一部例外を除き)、makeを最初に実行したディレクトリから移動しません。

なお、Linuxカーネルのmakeでは再帰的makeでよく見る「〜に入ります、〜から出ます」というメッセージが出ません。
これはMakefileの最初に
MAKEFLAGS += --no-print-directory
が書いてあるからです。
解析のときはこの部分を外してやると、なんども再帰的にmakeを呼び出していることがわかります。

LinuxカーネルのMakefileを解析する その2

前回は、LinuxのトップのMakefile の一番外のネスト
ifeq ($(skip-makefile),)
について説明しました。

その内部にさらにわかりにくい部分があります。

 トップのMakefile の400行目付近からは以下のようなコードになっています。


# To make sure we do not include .config for any of the *config targets
# catch them early, and hand them over to scripts/kconfig/Makefile
# It is allowed to specify more targets when calling make, including
# mixing *config targets and build targets.
# For example 'make oldconfig all'.
# Detect when mixed targets is specified, and make a second invocation
# of make so .config is not included in this case either (for *config).

no-dot-config-targets := clean mrproper distclean \
             cscope gtags TAGS tags help %docs check% coccicheck \
             include/linux/version.h headers_% \
             kernelversion %src-pkg

config-targets := 0
mixed-targets  := 0
dot-config     := 1

ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
    ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
        dot-config := 0
    endif
endif

ifeq ($(KBUILD_EXTMOD),)
        ifneq ($(filter config %config,$(MAKECMDGOALS)),)
                config-targets := 1
                ifneq ($(filter-out config %config,$(MAKECMDGOALS)),)
                        mixed-targets := 1
                endif
        endif
endif



Linuxのビルドにはまず、.configファイルを作らないといけません。
いきなり
$ make
とやっても .config ファイルがないよと怒られます。

普通は2回に分けて、
$ make oldconfig
$ make all
などとするところなのですが、
$ make oldconfig all
と1回でもうまくいきます。そのあたりの仕組みを説明します。

まず、makeのターゲットを3種類に分類しています。

(1) .configファイルを生成するためのターゲット (config / menuconfig / defconfig/ oldconfig など)
(2) .config がなくても実行できるターゲット (clean / mrproper / help など)
(3) .config がないと実行できないターゲット (all / vmlinux / modules など)


ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
    ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
        dot-config := 0
    endif
endif

の部分ですが、no-dot-config-targetsには(2)の.configファイルを必要としないターゲットが列挙されています。
MAKECMDGOALS が(2) のターゲットのみからなる場合、
dot-config := 0がセットされます。(.config ファイルはなくても実行できますよ、という意味。)
それ以外の場合は dot-config := 1です。

ifeq ($(KBUILD_EXTMOD),)
        ifneq ($(filter config %config,$(MAKECMDGOALS)),)
                config-targets := 1
                ifneq ($(filter-out config %config,$(MAKECMDGOALS)),)
                        mixed-targets := 1
                endif
        endif
endif

の部分ですが、MAKECMDGOALSが (1) .configファイルを生成するターゲットを含んでいる場合は
config-targets := 1
になります。
さらに、MAKECMDGOALSに (1)と(1)以外のターゲットが混ざっている場合は
mixed-targets := 1
になります。

dot-config, config-targets, mixed-targets の値によって、Makefileは以下のように分岐します。



ifeq ($(mixed-targets),1)
# ===========================================================================
# We're called with mixed targets (*config and build targets).
# Handle them one by one.

%:: FORCE
    $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@

else
ifeq ($(config-targets),1)
# ===========================================================================
# *config targets only - make sure prerequisites are updated, and descend
# in scripts/kconfig to make the *config target
   ...

config: scripts_basic outputmakefile FORCE
    $(Q)mkdir -p include/linux include/config
    $(Q)$(MAKE) $(build)=scripts/kconfig $@

%config: scripts_basic outputmakefile FORCE
    $(Q)mkdir -p include/linux include/config
    $(Q)$(MAKE) $(build)=scripts/kconfig $@

else
# ===========================================================================
# Build targets only - this includes vmlinux, arch specific targets, clean
# targets and others. In general all targets except *config targets.

...

ifeq ($(dot-config),1)
# Read in config
-include include/config/auto.conf

...

# If .config is newer than include/config/auto.conf, someone tinkered
# with it and forgot to run make oldconfig.
# if auto.conf.cmd is missing then we are probably in a cleaned tree so
# we execute the config step to be sure to catch updated Kconfig files
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
    $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig

   ...

else
# Dummy target needed, because used as prerequisite
include/config/auto.conf: ;
endif # $(dot-config)

  ...





mixed-target が1の時は、

%:: FORCE
    $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@

の部分しか見えませんが、これは1つ1つに分けて再帰的に呼び出しています。

$ make oldconfig all
は make oldconfig と make all の2回に分けて実行されるということです。


mixed-target が0で、config-targets が1のときは

config: scripts_basic outputmakefile FORCE
    $(Q)mkdir -p include/linux include/config
    $(Q)$(MAKE) $(build)=scripts/kconfig $@

%config: scripts_basic outputmakefile FORCE
    $(Q)mkdir -p include/linux include/config
    $(Q)$(MAKE) $(build)=scripts/kconfig $@


の部分が、*configターゲット の生成ルールになっています。


mixed-target が0で、config-targets が0のときが、それ以降の大部分のコードになります。

dot-config が1ならば、

-include include/config/auto.conf

のようにCONFIG_設定ファイルを読み込んでいます。
.config を直接 include するのではなくて、
make silentoldconfigによって .config から include/config/auto.conf を作り、それを読み込んでいます。

dot-config が0ならば、
include/config/auto.conf は必要ないのですが、空のルールだけは、
include/config/auto.conf: ;
のように書いてあります。
include/config/auto.conf がなくてもそのまま makeは進みます。

前回に引き続き、今回もトップのMakefileのわかりにくいネストを説明しました。

インデントをつけてわかりやすくまとめますと、

ifeq ($(mixed-targets),1)
  再帰的に1つずつ処理する
else
  ifeq ($(config-targets),1)
    .configを作る
  else
    ifeq ($(dot-config),1)
      include/config/auto.confを読み込む
    endif

    本体
  endif
endif

といった感じです。

Linux Kernel の Makefile を解析する その1

2012年2月8日水曜日

NFSサーバー立ち上げメモ

組み込みLinux / Android開発中はNFSブートが重宝したりします。
ファイルシステムをいちいちフラッシュROMに書き込まなくても、サーバー側の更新だけでいいですからね。

NFSサーバー側はRHELを使用しました。
/etc/exports
の設定は

/home/user1   192.168.10.10(sync)
/home/user2   192.168.10.20(sync)
/home/user3   192.168.10.30(sync)

みたいな感じでいけました。
192.168.10.* はそれぞれuser1~user3さんのボードに割り振ったIPアドレス。
-alldirsオプションはつけたらエラーになったので、いらないらしい。
上記の設定で /home/user*/ 以下の任意のディレクトリをマウントできるみたい。

それから、システム→管理→ファイアーウォールで
「信頼したインターフェース」でボードのつながっているethにチェックを入れとかないといけない。

NFSクライアント(ボード)側に与えるカーネルパラメータは
root=, nfsroot=, ip= あたりを設定。
設定の仕方は Documentation/filesystems/nfs/nfsroot.txt に書いてありました。

ARM Linux で start_kernel 関数までを追いかける

2012年2月2日木曜日

ARM Linux カーネルエントリー

組み込みLinux勉強中につき、メモ。

ARMのLinuxカーネルのエントリは arch/arm/kernel/head.S の ENTRY(stext)から始まる。
ブートローダーはメモリのどっかにカーネルイメージを置いてあげて、ENTRY(stext)の「物理アドレス」へジャンプする。「論理アドレス」ではない。

つまりカーネルの最初のあたりはPCは仮想アドレスを指しておらず、物理アドレスを指している。
This code is mostly position independent というコメントにあるように、基本的にposition independent なコードになっていて、必要な部分は仮想アドレス→物理アドレスに変換して動いてくれるようになっている。

ENTRY(stext) はカーネルイメージの先頭からある程度のオフセット(普通は0x8000)をつけたところから始まっている。

オフセット量を確認するにはカーネルのelf を逆アセしてもいいし、リンカースクリプトを見てもよい。

リンカースクリプトは
arch/arm/kernel/vmlinux.lds.S
をプリプロセッサに通して作られる
arch/arm/kernel/vmlinux.lds
だが、見てみると
例えば、


SECTIONS
{
 . = 0xC0000000 + 0x00008000;
 .text.head : {
  _stext = .;
  _sinittext = .;
  *(.text.head)
 }


みたいになっていれば、 オフセットは0x8000。
なので、ブートローダーはカーネルを置いた位置の先頭から + 0x8000のオフセット位置にジャンプすればよいことになる。



Kernelにjumpする前にブートローダーは
r0 = 0
r1 = machine nr
r2 = atags pointer

を設定しておく。

machine nr はmachineごとの固有の番号で、arch/arm/tools/mach-types に表がある。
atags pointerはよくわかりません。とりあえず0を入れておく。