2009年6月30日火曜日

query_apm_bios

今回は「query_apm_bios」関数を見てみます。
例によってBIOSにAPMの情報を問い合わせて、ブートパラメータ構造体に格納している関数に違いありません。

arch/x86/boot/apm.c
======
int query_apm_bios(void)
{
/* ~省略(変数宣言)~ */

/* APM対応かチェックする */
ax = 0x5300;
bx = cx = 0;
asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp ; setc %0"
     : "=d" (err), "+a" (ax), "+b" (bx), "+c" (cx)
     : : "esi", "edi");

/* ~省略(エラーチェック)~*/

/* いったんAPMから切断する */
ax = 0x5304;
bx = 0;
asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp"
     : "+a" (ax), "+b" (bx)
     : : "ecx", "edx", "esi", "edi");

/* Paranoia */
ebx = esi = 0;
cx = dx = di = 0;

/* 改めてAPM接続 */
asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp ; setc %6"
     : "=a" (ax), "+b" (ebx), "+c" (cx), "+d" (dx),
       "+S" (esi), "+D" (di), "=m" (err)
     : "a" (0x5303));

/* ブートパラメータ構造体にセット */
boot_params.apm_bios_info.cseg = ax;
boot_params.apm_bios_info.offset = ebx;
boot_params.apm_bios_info.cseg_16 = cx;
boot_params.apm_bios_info.dseg = dx;
boot_params.apm_bios_info.cseg_len = (u16)esi;
boot_params.apm_bios_info.cseg_16_len = esi >> 16;
boot_params.apm_bios_info.dseg_len = di;
/* ~省略~ */

/* もう一度APM情報を取得してくる */
ax = 0x5300;
bx = cx = 0;
asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp ; setc %0"
     : "=d" (err), "+a" (ax), "+b" (bx), "+c" (cx)
     : : "esi", "edi");
/*~省略(エラーチェック)~*/

/* バージョンとフラグ情報をセット */
boot_params.apm_bios_info.version = ax;
boot_params.apm_bios_info.flags = cx;
return 0;
}

======

なんだか似たようなコードばかり読んでいますが、こういった地道な作業の積み重ねなのです。

2009年6月29日月曜日

query_ist

main関数で「query_mca」の次にあるのは「query_voyager」ですが、NUMAシステム用なのでパスします。
その次の「query_ist」を見ていきます。
ISTとは「Intel Speedstep Technology」の略で、省電力機能です。
BIOSを使ってその辺の情報を拾ってくるんだろうな、と。。

同関数はmain.cの中にあります。
======
static void query_ist(void)
{
/* 古いBIOSでこの問い合わせするとクラッシュする */
if (cpu.level < 6)
return;

asm("int $0x15"
    : "=a" (boot_params.ist_info.signature),
      "=b" (boot_params.ist_info.command),
      "=c" (boot_params.ist_info.event),
      "=d" (boot_params.ist_info.perf_level)
    : "a" (0x0000e980),  /* IST Support */
      "d" (0x47534943)); /* Request value */
}

======
徐々にパターンが見えてきた気がしませんか。
問い合わせたい情報をレジスタにセットして0x15でソフトウェア割り込み。
とれた値を構造体に格納。
でもネットで調べてみてもISTを取得するためのBIOS機能なんて見つからず。
きっとここではそうやって取っているんだろうな、という解釈で進めますか、、、

2009年6月28日日曜日

query_mca

main関数もようやく中盤に差し掛かってきました。
今回見るのは「query_mca」。

arch/x86/boot/mca.c
======
int query_mca(void)
{
u8 err;
u16 es, bx, len;

/* BIOSを使ってROMテーブルを取得する */
asm("pushw %%es ; "
"int $0x15 ; "
"setc %0 ; "
"movw %%es, %1 ; "
"popw %%es"
: "=acd" (err), "=acdSD" (es), "=b" (bx)
: "a" (0xc000));

if (err)
return -1; /* No MCA present */

/* thread_info->addr_limitにesの値を格納する */
set_fs(es);
/* テーブルサイズを取得 */
len = rdfs16(bx);

if (len > sizeof(boot_params.sys_desc_table))
len = sizeof(boot_params.sys_desc_table);

/* ROMテーブルの内容をboot_params.sys_desc_tableにコピーする */
copy_from_fs(&boot_params.sys_desc_table, bx, len);
return 0;
}

======
int 0x15(AH:0xc0)でブートパラメータをとってきてboot_params_sysにコピーしている、というのがquery_mcaでやっていること。
で、これでとれてくるブートパラメータって何?というところは色々調べてみたのだけど分からずじまい。。。

この関数で呼んでいる「set_fs」「rdfs16」「copy_from_fs」といった関数はコメント以上のことは
やっていない単純なものなので見ないおきます。

2009年6月25日木曜日

keyboard_set_repeat

main関数で呼び出しているお次は「keyboard_set_repeat」。
コメントにもなぜここでやるのか分からん、と書いてあるような関数なので
モチベーションあがりませんが、短いので見ておきましょう。

======
static void keyboard_set_repeat(void)
{
u16 ax = 0x0305;
u16 bx = 0;
asm volatile("int $0x16"
: "+a" (ax), "+b" (bx)
: : "ecx", "edx", "esi", "edi");
}

======
キーボードのリピートレートをMAXに設定しています。
この部分はBIOSの機能としてはかなりマニアックなようでネット探しても
なかなか詳細な解説には出会えません。

linux-usersメーリングリストの過去ログから拾ってきました
http://search.luky.org/linux-users.3/msg02165.html

追記)
こちらも参考になります。
ブートローダー(その4)ーありえるえりあ

2009年6月24日水曜日

detect_memory

main関数で呼び出しているお次は「detect_memory」。
その名のとおり、メモリを検出しているに違いありません。

arch/x86/boot/memory.c
======
int detect_memory(void)
{
int err = -1;

if (detect_memory_e820() > 0)
err = 0;

if (!detect_memory_e801())
err = 0;

if (!detect_memory_88())
err = 0;

return err;
}

======

それでは各サブ関数を見ていきましょう。
すべて同じファイル内にあります。

======
static int detect_memory_e820(void)
{
/* ~変数宣言~ */

do {
size = sizeof(struct e820entry);

/* システムのアドレスマップをBIOSに問い合わせる。 */
asm("int $0x15; setc %0"
: "=d" (err), "+b" (next), "=a" (id), "+c" (size),
"=m" (*desc)
: "D" (desc), "d" (SMAP), "a" (0xe820));

/* 失敗してたらループを抜ける*/
if (err)
break;

/* BIOSによってはこのループ中にSMAPを返すのをやめてしまいます。
こちらとしてはここまで成功してんだか失敗してんだか
          ゴミまざってんだか分からないのでエントリ数0で
          ループ抜けちゃいます */
if (id != SMAP) {
count = 0;
break;
}

/* アドレスマップのエントリ数を加算 */
count++;
desc++;

/* アドレスマップエントリの配列サイズ(=128)を越えてないか? */
} while (next && count < e820_entries =" count;">

======
SMAPとか出てきますがこれは「System Memoy mAP」の略です。
BIOSからもらえるメモリマップの情報です。
要約してしまうとこの関数はBIOSの機能を使ってシステムのメモリマップを取得しているのです。
次に「detect_memory_e801」を見てみます。

======
static int detect_memory_e801(void)
{
/* ~変数宣言~ */

/* BIOSから拡張メモリ領域サイズ(64MBを超える部分)を取得する */
bx = cx = dx = 0;
ax = 0xe801;
asm("stc; int $0x15; setc %0"
: "=m" (err), "+a" (ax), "+b" (bx), "+c" (cx), "+d" (dx));

if (err)
return -1;

/* これ必要?(※原文まま)*/
if (cx || dx) {
ax = cx;
bx = dx;
}

/* とれてくる値が16MB超過していたらおかしい */
if (ax > 15*1024)
return -1; /* Bogus! */

/* とってきた64MBを超える分の拡張メモリサイズを格納しておく */
boot_params.alt_mem_k = (ax == 15*1024) ? (dx <<>


======
さっきから出てくる「int 0x15」というのはBIOSの機能を呼び出している部分で、
ファンクション表は英語のWikipediaが便利です。
あんまり難しい英語でもないのでなんとかいけます。

http://en.wikipedia.org/wiki/BIOS_call


他に親切な解説としては下記が詳しくて参考になります。
ブートローダー(その3) -ありえるえりあ


最後の方はコメントを無理やり訳してみましたが、正直よく理解できてません。
めげずに次へ

======
static int detect_memory_88(void)
{
u16 ax;
u8 err;

/* 拡張メモリサイズをとってくる */
ax = 0x8800;
asm("stc; int $0x15; setc %0" : "=bcdm" (err), "+a" (ax));

/* とってきたメモリサイズを格納しておく */
boot_params.screen_info.ext_mem_k = ax;
/* エラーの結果を返す */
return -err;
}

=======

というわけで「detect_memory」の内容をまとめると
1)「detect_memory_e820」でシステムメモリのアドレスマップエントリ数を取得
2)「detect_memory_e801」で64MBを超えた部分の拡張メモリサイズを取得
3)「detect_memory_88」で拡張メモリサイズを取得
ということをしているのです。

2009年6月23日火曜日

set_bios_mode

main関数でvalidate_cpuの次に呼び出しているのは「set_bios_mode」、今回はこちらを見ていきます。

=====================
static void set_bios_mode(void)
{
#ifdef CONFIG_X86_64
u32 eax, ebx;

eax = 0xec00;
ebx = 2;
asm volatile("int $0x15"
     : "+a" (eax), "+b" (ebx)
     : : "ecx", "edx", "esi", "edi");
#endif
}

=====================

、、、64bit CPUでないと何もしないようなのでおしまい。
CPUモードをBIOSに設定しているようです。

2009年6月22日月曜日

validate_cpu

main関数から呼び出している関数のうち、前回までにcopy_boot_params、init_heapの2つを
見てきました。今回は3つめ「validate_cpu」を見てみましょう。

arch/x86/boot/cpu.c
======
int validate_cpu(void)
{
  /*変数宣言(省略) */

  /* CPUのチェック */
  check_cpu(&cpu_level, &req_level, &err_flags);

  /* CPUレベルと要求レベルの比較 */
  if (cpu_level < req_level) {
    /* エラー処理(省略) */
  }

  /* エラーフラグが立っているか?*/
  if (err_flags) {
    /* エラー処理(省略)*/
  } else {
    return 0;
  }
}

=======

check_cpuという関数を呼び出して、関数内で設定された値をチェックして必要に応じてエラーメッセージを表示しているのみ。なので、実機能はcheck_cpuにあります。

arch/x86/boot/cpucheck.c
=======
int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr)
{
int err;

/* とりあえずCPUレベル3をセット */
memset(&cpu.flags, 0, sizeof cpu.flags);
cpu.level = 3;

/* アライメントチェックフラグがあればレベル4 */
if (has_eflag(X86_EFLAGS_AC))
cpu.level = 4;

/* CPUID命令でプロセッサを識別する。必要ならレベルも更新 */
get_flags();
/* エラーがないかチェックする */
err = check_flags();

/* 64bitかどうか */
if (test_bit(X86_FEATURE_LM, cpu.flags))
cpu.level = 64;

if (/*AMDの場合*/) {
~省略~
} else if (/* VIA C3の場合 */) {
~省略~
} else if (/* Transmetaの場合 */) {
~省略~
}

if (err_flags_ptr)
*err_flags_ptr = err ? err_flags : NULL;
if (cpu_level_ptr)
*cpu_level_ptr = cpu.level;
if (req_level_ptr)
*req_level_ptr = req_level;

return (cpu.level < req_level || err) ? -1 : 0;
}

=======

どんなCPUを使っているの?という情報をとってきてパラメータに格納しています。