livaの雑記帳

livaの雑記帳

OSとか作ってみたい

LinuxのCPU hotplugについて調べてみる。

諸事情によりLinuxのCPU hotplugについて調べてみたくなったので。

 

とりあえず試してみたいので、やり方を調べた。

NAKAMURA Minoru's Diary (2006年3月)

 

要するに、rootで以下のようにすれば良いとの事。

# echo 0 > /sys/devices/system/cpu/cpu1/online

/proc/cpuinfoからも見えなくなる。凄い。

そして1を突っ込むとまた復活する。凄い。

どういう仕組が知りたいので、調べてみる。

 

dmesgを読むと、

smpboot: CPU 1 is now offline

というのが出てるので、それっぽい関数にアタリを付ける。

arch/x86/kernel/smpboot.c内のnative_cpu_die()っぽい。

 

どういう経路でこいつが呼ばれてるか知りたいので、stacktraceを取得する。

kernhack.hatenablog.com

 

Linuxカーネルってこんな事できるのか。。。すごすぎでは。

 

#!/usr/bin/env stap

 

probe begin {

}

 

probe kernel.function("native_cpu_die") {

        print_backtrace()

        exit()

}

 

probe end {

printf("Done\n")

}

こんなスクリプトを書いて実行すると、以下のようなスタックトレースが取れる。

0xffffffff81040ee0 : native_cpu_die+0x0/0x80 [kernel]

0xffffffff816fa6be : _cpu_down+0x17e/0x2c0 [kernel]

0xffffffff816fa834 : cpu_down+0x34/0x50 [kernel]

0xffffffff81488bf4 : cpu_subsys_offline+0x14/0x20 [kernel]

0xffffffff81483ec5 : device_offline+0x95/0xc0 [kernel]

0xffffffff81483fc0 : online_store+0x40/0x90 [kernel]

0xffffffff814814b8 : dev_attr_store+0x18/0x30 [kernel]

0xffffffff8122f978 : sysfs_write_file+0x128/0x1c0 [kernel]

0xffffffff811b8c44 : vfs_write+0xb4/0x1f0 [kernel]

0xffffffff811b9679 : sys_write+0x49/0xa0 [kernel]

0xffffffff81719c6d : system_call_fastpath+0x1a/0x1f [kernel]

drivers/base/cpu.c内のcpu_subsys_offline()が呼ばれて、kernel/cpu.cのcpu_down()が呼ばれ、__cpu_die()経由でnative_cpu_die()が呼ばれる、と。

 

逆に起こす時は、cpu_subsys_online()からcpu_up()が呼ばれ、native_cpu_up()が呼ばれるという流れ。

native_cpu_up()はdo_boot_cpu(), wakeup_cpu_via_init_nmi()を経由して、wakeup_secondary_cpu_via_init()を呼び、MPの初期化が行われる。初期化されたコアのエントリポイントはstart_eipで指定される。このstart_eipはdo_boot_cpu()内で

real_mode_header->trampoline_startが指定されている。

 

trampoline_startは、arch/x86/realmode/rm/header.Sで定義されている通り、pa_trampoline_startである。

multibootカーネルにおける画面描画

multiboot specification(このブログでは全てv2を指す)に則ってカーネルを作るとブートローダ周りの諸々を手抜きできるのだけど、今回は特に画面描画辺りのHowToを軽くまとめておく。

 

最初にmultiboot specificationのおさらいをしておくと、

 

  • elfでカーネルを書ける
  • 仕様書に規定されている様々なハードウェア情報を勝手にブートローダが集めてくれる
  • 最初からモジュールをメモリに読み込ませたりできる(ディスクドライバがない状態でもモジュールがロードできる)

 

まあ最近はUEFIのお陰でブートローダは凄く書きやすくなったそうなのだけど、それでもmultiboot specificationは良くできているので、余程の事がないかぎりはこれで十分かなーというのが個人的な印象。

multiboot specficationをサポートしたgrubという素晴らしいブートローダがあるので、下周りは全てこいつに任せる事ができる。grubを使えば、BIOSUEFIファームウェアの差を吸収してくれるので、どのような環境でも等しく動くOSが作れる。特にBIOS環境下ではとても便利。

 

さて、multiboot specificationにはframebuffer info(仕様書3.6.12 Framebuffer info)というのが規定されており、画面描画のためのフレームバッファの情報を取得できる。取得できるのは、アドレスと解像度で、下の描画インターフェースがVBEだろうとGOPだろうとUGAだろうと同じように扱える。(描画インターフェースのサポートはgrub側の仕事なのだけど、どれもきちんと動く)

 

f:id:liva_h:20170506165924p:plain

ちゃんと公式のサンプルコードもあるよ。

kernel.c\doc - grub.git - GNU GRUB

 

で、問題はここから。

フレームバッファの情報が取れるのは良いのだけど、ブートローダ側がフレームバッファを使う準備をしない事にはフレームバッファに関する情報が得られない。その辺の設定が抜けていると時間が溶ける。

 

ブートローダはもうgrub2固定で説明する。

まずはgrub.cfgの設定。

 

insmod efi_gop

insmod efi_uga

insmod vbe

insmod vga

 

描画インターフェースのモジュール(つまりデバドラ)をgrubに読み込ませる必要がある。上2つはUEFI環境用、下2つはBIOS環境用。

 

次に、multiboot headerにframebuffer tagを置く。(参考:仕様書の3.1.10 The framebuffer tag of Multiboot2 header)multiboot headerというのは、elfカーネル内に埋め込む、カーネルからブートローダに情報を渡すための仕組み。シグネチャの直後とかに書けばよろし。

f:id:liva_h:20170506171100p:plain

 

これも公式サンプルにある。

boot.S\doc - grub.git - GNU GRUB

解像度は1024x768x32がたぶん無難。もっとデッカイ解像度が良い場合は数字を変えれば良いのだけど、必ずこの数値が使われるわけではないので、注意。(ブートローダの気まぐれとか、インターフェースのサポート状況とかによって低解像度に格下げされる)

この設定を埋め込まないと、ブートローダが勝手にデフォルトの解像度に設定してしまう。UEFIだと一応フレームバッファ(ただし解像度800x600x32)になるのだけど、BIOSだと80x25のテキストコンソールになると思う。

ただまあぶっちゃけテキストコンソールの方が便利だとは思う。UEFIではこれが削除されてしまって、自力でテキスト描画しなければならなくてちょっと不便。

 

ちなみに、自分でフォントを画面描画するなら、grubのpf2フォントを使うのが便利。普通にインストールすると /boot/grub/fonts/unicode.pf2 というのがあるはずなので、こいつをmoduleとして読み込んで、解析すればよろし。

raphine.hatenablog.com

 

こんな感じで描画される。

 

 

最後に、参考までに僕の自作OSのgrub.cfgを載せておく。

github.com

 

UEFI対応(その2)

これの続き

pf2フォントの解析ができたので、UEFIからブートしようと頑張る。

 

ACPICAの初期化でクラッシュするので原因究明

ここまでの問題のオチはこれ↑

 

 

RSDPが見つからない

たぶん返してくれてる、と思う。

これにてRSDPは解決。

 

HPETがない。

存在しなかったオチ。qemuUEFIでのデバッグは諦めた。

 

オマケ。

BIOS起動時もUEFI起動時と同様にフレームバッファベースで動かそうとして嵌った。

自作OSにおけるUEFI対応戦略

たまにはtwitterのまとめじゃなくて、ちゃんとした記事でも。

自作OS界隈でUEFI流行ってるというか、なんかUEFIすごーい!って崇めてる人が多いようなのでその雑感。

 

UEFIとは

最近主流のブートファームウェアBIOSの高機能版。

最近のUEFIマザーは大体BIOS後方互換性も実装している。

 

BIOSが互換実装されているというのは大きくて、正直今のマザーならBIOSからMBRブートしても何ら問題はない。

でもまあ、新しいデバイスに対応させたいというのはOSを自分で書きたい人の夢だし(たぶん)、何時後方互換が無くなるかも分からないので、UEFI対応するリソースがあるなら、移るべき。

 

UEFIアプリとして作るべき?

UEFIファームウェアは各種デバイス等を扱う関数を提供していて、ネットワークデバイスさえ簡単に扱えるし、プロトコルスタックも実装されているから、もうすぐにパケットでおしゃべりできて凄いらしい。(使った事はない)

なので、UEFIの豊富なハードウェアインターフェースを活用する事で、簡単にOSが自作できると主張する人もいる。確かにそれは1つの戦略としてアリではあるとは思う。

 

自分でOS作りたい人の悩みとして、世の中に星の数程存在するハードウェアを一々サポートしていたらとてもではないけれど間に合わない、というのがある。(昨今のそこそこ大きなハードウェアのデバドラを自力で書くだけの実力がない、という理由もあるだろう)

「OSを実装したい」人には、「アプリケーション向けの抽象化インタフェースを作りたい人」と「ハードウェアを制御したい人」という二種類の人種が存在すると僕は考えている。このうちの前者に相当する人なら、ハードウェアのサポートはあくまでユーザーの利便性のために渋々実装しなければならない類の事なので、デバドラの実装をUEFIファームウェアに投げてしまうという近道を通るのは1つの手だと思う。

ちなみに、僕は後者の「ハードウェアを制御したい人」なので、ちょっとそういうのとは相容れない感じの人間。僕は組み込みシステムとかはあんまり触りたくないのだけど、意外と組み込み屋さんに思考が近いのかなーなんて最近思い始めた。ただし、ハードウェアを制御するのはOSというよりはデバドラなので、厳密には僕みたいな人は「OSを実装してる」人ではないのかもしれない。

あと、そうそう「OSを自作したい人」の中には「ウィンドウを描画したり、GUIかっこよくしたりしたい」という人が一定数いるのだけど、それはOSではなくウィンドウマネージャなので、gnomeとか作れば良いのではないでしょうか。

 

さて、UEFIファームウェアで楽をしたい人はそれはそれで良いと思っているのだけど、UEFIでデバイス触って遊んでても一切独自性はないので、そういう人は早く独自の抽象化インターフェースを作るのに進むのが良いと思います。

「デバイスとかは直接触りたいんだけど、UEFIを通せば簡単だから、UEFI越しに触ればいいや」、ってのは意味不明なので、デバイスを触りたい人はUEFIを崇めてないで、地道にデバドラ書こう。それしかデバイスを触る近道はない。

 

UEFIアプリはあくまでプログラムローダー

UEFIのコンソーシアム的にはUEFIアプリってのはあくまでブートローダーとして使って欲しいらしい、という事を耳にした。最近の殆どのOSは自前でハードウェアを制御するから、ブートローダーとしてのみUEFIを使えれば良い。僕もそれが正しいと思う。

くどいけど、UEFIを使ってOSを作るという方向性自体は否定しない。OSを作ってやりたい事ってのがデバイスを弄る事以外であるならね。

僕みたいなデバイスを直接弄りたい系の人は、さっさとExitBootServices()(だっけ?)を読んで、UEFIを抜ければ良い。結局UEFI経由でデバイスを触るのは楽だけども、デバイスを直接触れてないから、性能を最大限引き出したりする用途では使えない。ExitBootServices()呼び出すまではUEFIがデバイスを握ってるから、OSから制御する事もできないし。(たぶん。触ってないので、もしかしたら間違ってるかも)

 

grub-efiいいよ

UEFIgrubを殺すのだ、という話も聞いた。もしかしたらUEFIコンソーシアム的にはそうなのかもしれない。でも、フルスクラッチでOS作ってる人程grubを使えば良いと思う。

UEFIアプリを使うとめちゃくちゃローダーが簡単に書けるらしくて、たしかにそれは凄い。grubってめちゃくちゃ大規模だけど、それ相当の物が自分で作れてしまうのは革新的だとは思う。でも、結局ローダーである以上、ローダーとカーネルの間の橋渡しインターフェースとかは自分で考えなきゃいけないわけで、その労力はデカい。

OSを作りたい人はOSが作りたいのであって、ローダーとカーネルの間の橋を作りたいわけではない。本当にそういうのを作りたいのであれば、OSではなくてgrubを置き換える素晴らしいブートローダーを書けば良いだけ。

「全て自分で作りたいからOSを作るんだ!」という人もいて、その心意義は凄いし、見習わねばとは思うし、言いたい事も分からないではないのだけど、残念ながら僕のような非凡な人間にはそんな事をやっている暇はない。OSを作るので精一杯。あと、時々「ハードウェアはUEFIがあるから全部使えるようになるんだ、これでついに全部ゼロからOSを作れる!」という人がいるけど、それは流石によくわからん。自分で全部作りたいと言ってる割にUEFIファームウェアにおんぶに抱っこだよそれ、みたいな。

 

というわけで、ファイルシステム設計したり、プロセス構造実装したり、みたいな目的で自作OSやっているのでないなら、grub使うのが近道なのではなかろうか。UEFIアプリで自作ローダーを作るのは自分のOSがそこそこ大きくなってきて、grubだとやりたい事ができない、ってなった時だと思うなぁ。

 

grubのpf2ファイルの解析

UEFIサポートによりフレームバッファにテキストを描画しなければならなくなったので、grubのpf2ファイルを使って描画する事にしてみた。

UTF-32ってのが良い。vga/textだとasciiしか描画できない。

 

 

日本語表示する機会なんてたぶんないけど、描画できる事は大事。

UEFI対応(その1)

諸事情により、UEFIに対応したい。

現状grub-pcからのmultiboot2仕様による起動なので、grub-efiに載せ替えれば動くだろと思ったら動かなかった。

 

とりあえずこれでgrub-uefiからの起動には成功。次は画面出力。

 

 

画面出力はできたのだけど、これまでvga/textに描画してたのが、フレームバッファになってしまったので、フォント描画をやらなければならず、ツライ。その話は別記事で。


続きはこっち

自作osでlinuxのhello worldバイナリを動かす

elfのロードまではできているので、実際に実行して、linuxシステムコールを処理する云々の話。