2009年8月31日月曜日

page_address_init

今日はpage_address_initを見てみましょう。

実体はmm/highmem.cにあります。

=====
void __init page_address_init(void)
{
int i;

INIT_LIST_HEAD(&page_address_pool);
for (i = 0; i < ARRAY_SIZE(page_address_maps); i++)
list_add(&page_address_maps[i].list, &page_address_pool);
for (i = 0; i < ARRAY_SIZE(page_address_htable); i++) {
INIT_LIST_HEAD(&page_address_htable[i].lh);
spin_lock_init(&page_address_htable[i].lock);
}
spin_lock_init(&pool_lock);
}

=====

ページアドレスマップのリストとハッシュテーブルを初期化しています。
ページとアドレスの対応付けを管理する変数なのでしょう。

2009年8月28日金曜日

boot_cpu_init

今回はboot_cpu_initを見ていきます。

実体はstart_kernelと同じinit/main.cにあります。

======
static void __init boot_cpu_init(void)
{
/* カレントCPUのプロセッサIDを持ってくる */
int cpu = smp_processor_id();
/* Mark the boot cpu "present", "online" etc for SMP and UP case */
set_cpu_online(cpu, true);
set_cpu_present(cpu, true);
set_cpu_possible(cpu, true);
}

======

プロセッサIDはどこから拾ってくるのかsmp_processor_idを追ってみたけれど、スタックポインタの前あたりに置いてあるようです。いまいち追いきれていません。。
あとはビットを立てているようで、これはレジスタのbitなのかなと思ったのだけどどうやらカーネル内部で持っている構造体?のメンバのようなのだけど、ちっとも追えません。

ちょっと私には難易度高かったです。

2009年8月26日水曜日

tick_init

お次はtick_initを見てみます。
tickを初期化しているのでしょう。

実体はkernel/time/tick-common.cにあります。
======
/**
* tick_init - initialize the tick control
*
* Register the notifier with the clockevents framework
*/
void __init tick_init(void)
{
clockevents_register_notifier(&tick_notifier);
}

======

時間計測のイベントを受け取るための登録をしています。
clockevents_register_notifierは同じ階層のclockevents.cにあります。

======
/**
* clockevents_register_notifier - register a clock events change listener
*/
int clockevents_register_notifier(struct notifier_block *nb)
{
int ret;

spin_lock(&clockevents_lock);
ret = raw_notifier_chain_register(&clockevents_chain, nb);
spin_unlock(&clockevents_lock);

return ret;
}

======

こうしてイベントを通知してもらうためのリスナーチェインに登録しています。
この辺の仕組みはまた別の機会に、、、

2009年8月24日月曜日

lock_kernel

start_kernelは少し進みます。

======
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
lock_kernel();
tick_init();
boot_cpu_init();
page_address_init();
printk(KERN_NOTICE);
printk(linux_banner);
setup_arch(&command_line);
mm_init_owner(&init_mm, &init_task);
setup_command_line(command_line);
unwind_setup();
setup_per_cpu_areas();
setup_nr_cpu_ids();
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */

======

今回はlock_kernelを見てみましょう。
実体は「__acquires」という名前でarch/x86/kernel/cpu/mtrr/generic.cにあります。

======
static void prepare_set(void) __acquires(set_atomicity_lock)
{
unsigned long cr0;

/* スピンロックを獲得 */
spin_lock(&set_atomicity_lock);

/* CR0レジストをCD=1、NW=0の状態にする */
cr0 = read_cr0() | X86_CR0_CD;
write_cr0(cr0);
wbinvd();

/* グローバルページ機能をDisableにする */
if ( cpu_has_pge ) {
cr4 = read_cr4();
write_cr4(cr4 & ~X86_CR4_PGE);
}

/* TLBをクリア */
__flush_tlb();

/* MTRRの値を読み取る */
rdmsr(MTRRdefType_MSR, deftype_lo, deftype_hi);

/* MTRRを無効にしてuncachableに設定する */
mtrr_wrmsr(MTRRdefType_MSR, deftype_lo & ~0xcff, deftype_hi);
}

======

まあ、なんか、いろいろとクリアしているみたいです。

2009年8月21日金曜日

early_init_irq_lock_class

前回のつづきで、「early_init_irq_lock_class」を見てみましょう。

実体はkernel/irq/handle.cにあります。

======
void early_init_irq_lock_class(void)
{
int i;

for (i = 0; i < NR_IRQS; i++)
lockdep_set_class(&irq_desc[i].lock, &irq_desc_lock_class);
}
======

ロックの整合性を検証するlockdepのためにロックのマッピング情報を初期化しています。
ここもこれだけ。

early_boot_irqs_off

前回のつづきで、「early_boot_irqs_off」を見てみましょう。

実体はkernel/trace/trace_irqoff.cにあります。

======
void early_boot_irqs_off(void)
{
}
======

空です。。

local_irq_disable

前回からstart_kernelの位置は少し進みます。

======
local_irq_disable();
early_boot_irqs_off();
early_init_irq_lock_class();
======

今日はlocal_irq_disableを見てみましょう。こちらはdefineで切られていて、実際に処理するのは「native_irq_disable」関数です。

include/asm-x86/irqflags.h
=====
static inline void native_irq_disable(void)
{
asm volatile("cli": : :"memory");
}
=====

cliで割り込みを禁止しているだけです。最後の「memory」はコンパイラにメモリの場所を変更することを伝えています。なぜこれが必要なのかは理解できていません。。。

2009年8月20日木曜日

cgroup_init_early

今日も前回のつづきです。

======

/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
lockdep_init();
debug_objects_early_init();
cgroup_init_early();

======

今回はcgroup_init_earlyを見ていきましょう。
実体はkernel/cgroup.cにあります。

======
int __init cgroup_init_early(void)
{
int i;
kref_init(&init_css_set.ref);
kref_get(&init_css_set.ref);
INIT_LIST_HEAD(&init_css_set.cg_links);
INIT_LIST_HEAD(&init_css_set.tasks)
INIT_HLIST_NODE(&init_css_set.hlist);
css_set_count = 1;
init_cgroup_root(&rootnode);
list_add(&rootnode.root_list, &roots);
root_count = 1;
init_task.cgroups = &init_css_set;

init_css_set_link.cg = &init_css_set;
list_add(&init_css_set_link.cgrp_link_list,
&rootnode.top_cgroup.css_sets);
list_add(&init_css_set_link.cg_link_list,
&init_css_set.cg_links);

for (i = 0; i < i =" 0;" ss =" subsys[i];">early_init)
cgroup_init_subsys(ss);
}
return 0;
}

=====

cgroupというのはControl Groupの略でカーネル2.6.25で追加されたリソース管理の仕組みです。以下の記事に詳しい解説があります。

Linux Kernel Watch
http://www.atmarkit.co.jp/flinux/rensai/watch2008/watch05a.html

ここではcgroupを管理するためのツリーを初期化しています。

2009年8月17日月曜日

debug_objects_early_init

前回のつづきです。

======
/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
lockdep_init();
debug_objects_early_init();
cgroup_init_early();
======

今回はdebug_objects_early_initを見ていきましょう。
実体はkernel/lib/debugobjects.cにあります。

======
void __init debug_objects_early_init(void)
{
int i;

for (i = 0; i < ODEBUG_HASH_SIZE; i++)
spin_lock_init(&obj_hash[i].lock);

for (i = 0; i < ODEBUG_POOL_SIZE; i++)
hlist_add_head(&obj_static_pool[i].node, &obj_pool);
}
======

debugobjects.cというファイルがどうもデバッグ用オブジェクトを管理するためのインフラを提供するためのもののようで、ここではその初期化をしているようです。初期化せずに使ってますとか、獲得したけど解放していません、といったことを検出するための仕組みのようです。
ちょっと自信ないけど。。。

2009年8月14日金曜日

lockdep_init

解凍したカーネルのジャンプ先をソースや諸々の設定ファイルから読み取るのは断念中なのですが、とにもかくにも解凍しおえたカーネルのジャンプ先はarch/x86/kernel/head_32.Sの「startup_32」になります。※
同名ファイルの同名関数を過去に見ていますが、それとは別物です。この辺りのアセンブラは勉強して再チャレンジということにして、ここではパスします。このstartup_32ではページテーブルに関して設定しています。そしてここからジャンプする先がinit/main.cにある「start_kernel」です。
今後はここを起点にソースを見て行くことにしましょう。

Inside the Linux boot process

「start_kernel」はとても長いので少しずつ見て行きましょう。
======
asmlinkage void __init start_kernel(void)
======

「__init」キーワードは一度実行したらメモリから消すように指示します。「asmlinkage」というキーワードはこれがシステムコールであることをコンパイラに
伝えるためのものです。
詳細は以下が参考になります。

はじめてのカーネルソース
ITpro 第3回 ソース内のシステム・コールを確認する

======
char * command_line;
extern struct kernel_param __start___param[], __stop___param[];

smp_setup_processor_id();
======

ローカル変数宣言のあと、smp_setup_processor_idをコールしますがSMP向けなのでパス。

======
/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
lockdep_init();
debug_objects_early_init();
cgroup_init_early();
======

まずはここ「lockdep_init」から。
lockdep_initの実体はkernel/lockdep.cにあります。
=====
void lockdep_init(void)
{
int i;

/*
* Some architectures have their own start_kernel()
* code which calls lockdep_init(), while we also
* call lockdep_init() from the start_kernel() itself,
* and we want to initialize the hashes only once:
*/
if (lockdep_initialized)
return;

for (i = 0; i < CLASSHASH_SIZE; i++)
INIT_LIST_HEAD(classhash_table + i);

for (i = 0; i < CHAINHASH_SIZE; i++)
INIT_LIST_HEAD(chainhash_table + i);

lockdep_initialized = 1;
}

=====

ハッシュテーブルを初期化しているようですが、このハッシュテーブルは何者でしょうか。調べてみるとlockdepというのはカーネル内のロックが正しく使われているか実行時に検証する仕組みのようです。カーネルソースに含まれているDocumentation/lockdep-design.txtに詳細があります。
※JFではまだ日本語化されていないようです

2009年8月10日月曜日

decompress_kernel

前回、decompress_kernelをコールして圧縮されたカーネルを解凍するところまで見ました。
decompress_kernelで解凍してもらっている圧縮された部分を探してみよう、というのが
今回の予定でした。
が、、、調べてみてもどうにも理解できず。
力不足です。

今後のためにどこまで分かって何が分からないかをメモしておきます。

decompress_kernelの入力として渡している解凍元は以下のように指定されています。
====
leal input_data(%ebx), %eax
====

input_dataを探してみると、多分以下の部分に当たると思います。

arch/x86/boot/compressed/vmlinux.scr
====
SECTIONS
{
.rodata.compressed : {
input_len = .;
LONG(input_data_end - input_data) input_data = .;
*(.data)
output_len = . - 4;
input_data_end = .;
}
}
====

そこでリンク時にこのvmlinux.scrを参照しているのはどこだろうかとMakefileを
調べてみます。

arch/x86/boot/compressed/Makefile
====
$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.gz FORCE
$(call if_changed,ld)
====

piggy.oというのがinput_dataに相当するのかな、と想像。
この元になっているvmlinux.bin.gzの出所を順々に調べてみます。

====
$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE
$(call if_changed,gzip)
====
$(obj)/vmlinux.bin: vmlinux FORCE
$(call if_changed,objcopy)
====
$(obj)/vmlinux: $(src)/vmlinux_$(BITS).lds $(obj)/head_$(BITS).o $(obj)/misc.o $(obj)/piggy.o FORCE
$(call if_changed,ld)
====

また、piggy.oに帰ってきてしまった。。
想像に想像を重ねたような推測で進めているので、途中の想定で大きな方向間違いがあるのかもしれません。
この部分についてはまたいずれ、出直してきます。。。

2009年8月3日月曜日

relocated

前回のつづきでhead_32.Sのrelocatedラベルの処理を見てみましょう。

arch/x86/boot/compressed/head_32.S
=============
.section ".text"
relocated:

/*
* BSSセクションをクリア
*/
xorl %eax,%eax
leal _edata(%ebx),%edi
leal _end(%ebx), %ecx
subl %edi,%ecx
cld
rep
stosb

/*
* デコンプレッサ用にスタックをセット
*/
leal boot_stack_end(%ebx), %esp

/*
* パラメータをセットして、decompress_kernel関数をコールする
*/
movl output_len(%ebx), %eax
pushl %eax
pushl %ebp # output address
movl input_len(%ebx), %eax
pushl %eax # input_len
leal input_data(%ebx), %eax
pushl %eax # input_data
leal boot_heap(%ebx), %eax
pushl %eax # heap area as third argument
pushl %esi # real mode pointer as second arg
call decompress_kernel
addl $20, %esp
popl %ecx

=============
ここでやっているのはまずBSSセクションをクリアしてスタックをセットした後、decomoress_kernel関数を呼び出すというものです。
decompress_kernelコール前には解凍先アドレス、解凍元アドレス、サイズを引数として渡すためにスタックに積んでいます。