xv6-armv7
『人口知能・人類最悪にして最後の発明』(ダイヤモンド社)なんて本を読んだ。
この分野の人気者は、Lispの父マッカーシー先生でもなければ、マービン・ミンスキー教授でもなく、 レイ・カーツワイルらしい。彼はブラックホールであるググルXに飲み込まれて、いろいろと やっているらしい。
そして彼のご宣託によれば、人口知能の能力が2045年に人間のそれを超えるそうだ。 技術的特異点(シンギュラリティ)ってのがあって、それを超えると自己成長が指数関数的に 速まる。
昔、2001年宇宙の旅ってSF映画があって、そこにHAL9000ってコンピュータが出てきた。主人公の 公の少年に説教して、うるさく思った少年が電源プラグを抜こうとすると、それだけはヤメテって 懇願してた。シンギュラリティを一旦超えてしまうと、そういうのどかな事も無くなるらしい。
だから、最悪にして最後の発明。原子力が平和にも戦争にも役立った、、けど、今は何とか 平衡状態を保っている。人口知能はそんなものでは無いと言う。
金さえつぎ込めば、その臨界点に早く到達出来る。世界を握れる。だから、国防省が膨大な 予算をつぎ込んでいる。金が余っているウォール街の某は、金儲けマシンを一生懸命に 開発してる。富が一箇所に集まるようになったら、その分野の人口知能は技術的特異点を 超えたと思って間違いないだろう。
国防省は、勿論兵器への応用、戦争勝利への方法論と実践が最終目標。何もドンパチだけが 戦争にあらず。相手国のインフラを破壊するだけで勝利決着。ある試算によると、送電網を 混乱させるだけで、1歳以下の乳幼児は2週間以内に離乳食が食べれなくなって(製造も輸送も医療も みんな電気のおかげ)死亡。成人でも1年以内に90%の人が死ぬと言う。
こういう迷惑はマルウェアにおまかせ。サイバー兵器をイランに適用して、原子力開発を STOPさせたのは、USAのDが仕掛けたテロですよ。恐いねぇ。なんでも、シーメンス製の コントローラを狙ったとか。オムロン製とかのシーケンサーは大丈夫か?
これもそれも、コンピュータの能力向上が寄与してるのはまちがいない。第二世代のipadは 一昔のスパコンの能力をとっくに超えているとか。一昔のスパコンと言うと、世界一高価な 椅子こと、CRAY-1しか思い付かないんだけど、どうよ。 CRAY manualでも、眺めて みますかな。 2240004C_CRAY-1_Hardware_Reference_Nov77.pdf いろいろあるけど、まずはハードから。
前回だかに書いたやつ、みんな気になるね。 国勢調査、37%の世帯がオンラインで回答 日本もやっとネットに関しては韓国に近づいた?
iOS 9へのアップグレード、一部で端末が文鎮化 不幸は続くよ、どーこまでも!!
xv6
なにげに、 カーネル/VM探検隊なんて集積地を見ている。 みなさんお盛んで。んなわけで、昔やったuv6なんてのも出てくるんですが、、、、ちらっと xv6も言及されたような。。。 オイラーも2013年にやってたね。またひっくり返してみるか。
本家はこちら、 Xv6, a simple Unix-like teaching operating system ウブだとすいすいかと思ったら、起動の段階でSDLが無いとか言われた。どうする? Running qemu remotely (via ssh) そんなVGAなカードは無視するのが鉄則。
そんなこんなで、起動スクリプトは下記。某OpenBSDの起動スクリプトをパクッタだけですけど。
qemu-system-i386 -m 256M -net nic -net user -s -nographic \ -redir tcp:2022::22 -hdb fs.img xv6.img
本来、make qemuすれば起動してくるんだけど、こちらは豪華にsmpなマシン用に なってるんで、貧乏な人は上記で十分。
xv6はそもそもuv6を移植したもの。pdp-11で動いてたやつね。古典を読まないでどうするって、 Plan9日記も言いつつ、微妙なトーン。 綺麗にPlan9はどうでっしゃろっと仰ってる。なんでも、Plan9の一部がxv6に取り込まれてる とか。どこだろう?
もう一度、 xv6ソースコードリーディング かなあ。糞石32のせいで、脳に余計な負荷がかかるよ。そういう場合は、
xv6-armv7
カーネル探検隊の過去ログを見てたら、各種CPUの変態度と処理能力の関係図が出てた。 それによると、処理能力が高いけど変態度が一番高いのは、糞石でした。次に酷い変態度の 石はarmでしたよ。変態が世の中を席巻してるね。
素直で良い子は、PowerPCとMIPSだった。オイラーもそう思うぞ。残念ながら、良い子は 長く生き長らえる運命になにのね。処理能力は落ちるけど素直なのもいて、ルネサスのshが 上がっていたね。激同意。
糞石から逃れるとしたら、次の候補は消極的選択でarmになるな。ラズパイも流行って いるし、そこらのねーちゃんが持ってるスマホにも入ってるしね。
奇特な方が、 xv6-armv7を公表されてます。そこからFORKして ラズパイ用もあるようです。試しに入れてみました。
debugが出来ればいいなってんで、
sudo apt-get install gdb-arm-none-eabi cd /usr/bin sudo ln -s arm-none-eabi-gdb agdb
を入れました。arm用のgdb名が長いので短い名前も登録しました。 お決まりのgdb用設定をしてからrun-debug.sh を走らせておいて、M-x gdbして、
Run gdb (like this): agdb -i=mi kernel.elf
すれば
(gdb) bt #0 0xc0020624 in pushcli () at arm.c:59 #1 0xc0025e2c in acquire (lk=0xc00ad60c <ptable>) at spinlock.c:29 #2 0xc0025880 in scheduler () at proc.c:330 #3 0xc00248a8 in kmain () at main.c:54 #4 0x800104e8 in start () at start.c:195 Backtrace stopped: previous frame inner to this frame (corrupt stack?)
ふーん、こういう所を回っているんだ。emacsからgdb使うのはヤダって場合は、debug.shを 素直につかいます。ただ、ちょっと改変が必要でしたが。
sakae@uB:~/xv6-armv7/src$ cat debug.sh agdb -q -n -x connect.gdb kernel.elf
やはり、run-debug.shを走らせておいてから、debug.shを起動します。
sakae@uB:~/xv6-armv7/src$ ./debug.sh Reading symbols from kernel.elf...done. 0x80010000 in _start () (gdb) b start Breakpoint 1 at 0x800103f8: file start.c, line 169. (gdb) c Continuing. Breakpoint 1, start () at start.c:169 169 _puts("starting xv6 for ARM...\n"); (gdb) n 173 set_bootpgtbl(PHY_START, PHY_START, INIT_KERN_SZ, 0);
このあたりからハードウェアのセットアップが始まるのかな。init386()相当だな。 後でじっくり見るとして、先へ進みます。
(gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. 0xc0025894 in scheduler () at proc.c:333 333 if(p->state != RUNNABLE) { (gdb) p *p $2 = { sz = 0, pgdir = 0x0, kstack = 0x0, state = UNUSED, pid = 0, parent = 0x0, tf = 0x0, context = 0x0, chan = 0x0, killed = 0, ofile = {0x0 <repeats 16 times>}, cwd = 0x0, name = '\000' <repeats 15 times> }
これが、大事な大事なプロセスの雛形か。これの意味する所を理解すればいいんだな。 ああ、コードをチラ見してたら、
$ 1 sleep init 2 sleep sh procdump: 15: 0x0 14: 0x0 13: 0x0 12: 0x0 11: 0x0 10: 0x0 9: 0x0 8: 0x0 7: 0xc0011fbc 6: 0xc0027574 5: 0xc00276d0 4: 0xc00293a4 3: 0xc0028be4 2: 0xc0021be4 1: 0xc0025dc4
CTRL-p で、プロセスリストとスタックの内容を表示してくれるのね。ソース読むと徳するぞ。
それはそうと、 どんな風にイメージを作っているかとmakeのログを紐解くと
../tools/mkfs ../build/fs.img _cat _echo _grep _init _kill _ln _ls _mkdir _rm _sh _stressfs _usertests _wc _zombie UNIX used 29 (bit 1 ninode 26) free 29 log 10 total 1024 balloc: first 510 blocks have been allocated balloc: write bitmap block at sector 28 arm-none-eabi-objdump -S usys.o > usys.asm rm wc.o grep.o mkdir.o rm.o ln.o stressfs.o kill.o echo.o init.o zombie.o cat.o sh.o usertests.o ls.o make[1]: Leaving directory '/home/sakae/xv6-armv7/src/usr' cp -f build/initcode initcode cp -f build/fs.img fs.img LINK kernel.elf arm-none-eabi-objdump -S kernel.elf > kernel.asm arm-none-eabi-objdump -t kernel.elf | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > kernel.sym rm -f initcode fs.img
一番面白いリンクの所が隠されているな。Makefileを覗くと何やらcallなんてのを使ってる。 なんじゃらほいと思って調べてみると、 8.6 callファンクション なんか、おいらには悪夢だな。いつの間にrakeに喧嘩売るようになった。ちっともLLじゃないぞ。 全く、GNUの連中といったら節度を知らないな。しょうがないので、脳内変換すると、
kernelは普通のアプリケーション、その後ろにfs.imgをくっつけたものなんだな。これって、 組み込みの方式か。
sakae@uB:~/xv6-armv7/src$ arm-linux-gnueabi-nm kernel.elf | grep fs_img c00aa0c0 D _binary_fs_img_end 00080000 A _binary_fs_img_size c002a0c0 D _binary_fs_img_start
これが使われている所は、memide.c。あこがれのメモリーDISKだ。そこらのSSDよりも速いかな。 そんな事は無いだろう。qemu上のメモリーだからな。
まてまて、一応makeのコマンド調べて、使えそうなオプションを試してみると、
sakae@uB:~/xv6-armv7/src$ make clean sakae@uB:~/xv6-armv7/src$ make -n echo " CC build/device/gic.o" && arm-none-eabi-gcc -march=armv7-a -mtune=cortex-a15 -fno-pic -static -fno-builtin -fno-strict-aliasing -Wall -Werror -I. -g -c -o build/device/gic.o device/gic.c echo " AS build/entry.o" && arm-none-eabi-gcc -march=armv6 -c -o build/entry.o entry.S echo " AS build/initcode.o" && arm-none-eabi-gcc -march=armv6 -nostdinc -I. -c -o build/initcode.o initcode.S echo " LINK build/initcode" && arm-none-eabi-ld -L. -N -e start -Ttext 0 -o build/initcode.out build/initcode.o echo " OBJCOPY build/initcode" && arm-none-eabi-objcopy -S -O binary --prefix-symbols="_binary_build/initcode" build/initcode.out build/initcode arm-none-eabi-objdump -S build/initcode.o > initcode.asm make -C tools make -C usr cp -f build/initcode initcode cp -f build/fs.img fs.img echo " LINK kernel.elf" && arm-none-eabi-ld -L. -T kernel.ld -o kernel.elf build/lib/string.o build/arm.o build/asm.o build/bio.o build/buddy.o build/console.o build/exec.o build/file.o build/fs.o build/log.o build/main.o build/memide.o build/pipe.o build/proc.o build/spinlock.o build/start.o build/swtch.o build/syscall.o build/sysfile.o build/sysproc.o build/trap_asm.o build/trap.o build/vm.o build/device/timer.o build/device/uart.o build/device/gic.o build/entry.o /usr/lib/gcc/arm-none-eabi/4.8/libgcc.a -b binary initcode fs.img arm-none-eabi-objdump -S kernel.elf > kernel.asm arm-none-eabi-objdump -t kernel.elf | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > kernel.sym rm -f initcode fs.img
やっぱりね。-nは別名 --just-print 動きを表示だけするって事で、こんな所に展開方法が 隠れていたよ。ああ、これですっきりしたよ。
起動の流れ
どんな風に起動するか?
kernel.elfを作る、リンカースクリプト、kernel.ldを見ると、冒頭に、
OUTPUT_FORMAT("elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) ENTRY_SVC_STACK_SIZE = 0x1000; SECTIONS { /* the entry point, before enabling paging. The code to enable paing needs to have the same virtual/physical address. entry.S and start.c run in this initial setting.*/ /* . = 0x10000; */ . = 0x80010000;
ふむ、ページング機構がまだ働いていないんで、仮想/物理アドレスは等しいよ。entry.Sを 見てから、start.cを見れ。
その前に、このプログラム(kernel.elf)はどうやってメモリーにロードされるの? いわゆるローダー問題。 それは、qemu様を起動する時、-kernelオプションを付けているから、qemuがやってくれる んだよ。糞石の場合は、IBMと結託してやれmbrとか五月蝿い事があるんだな。
entry.Sではbssをクリアして、カーネルスタックをセットしてから、start.c/startへと 行くんだな。startの中では、
void start (void) { uint32 vectbl; _puts("starting xv6 for ARM...\n"); // double map the low memory, required to enable paging // we do not map all the physical memory set_bootpgtbl(PHY_START, PHY_START, INIT_KERN_SZ, 0); set_bootpgtbl(KERNBASE+PHY_START, PHY_START, INIT_KERN_SZ, 0);
いきなり、シリアルポートを叩いて大丈夫ってのはあるけど、取りあえず動き始め ましたよのご挨拶。そしてそろそろとページングを有効にしてきます。 そして、ちゃんと設定が済んだら、それを有効にして、スタックも仮の場所から 正式な場所に変更、通常のBSS領域をクリアしてから、main.c/kmainへと飛んで行く。
kmainの中では、メモリー系のイニシャライズ、トラップベクターの設定、armの割り込み 関係gicのイニシャライズ、ええい面倒なので
: kmem_init (); kmem_init2(P2V(INIT_KERNMAP), P2V(PHYSTOP)); trap_init (); // vector table and stacks for models gic_init(P2V(VIC_BASE)); // arm v2 gic init uart_enable_rx (); // interrupt for uart consoleinit (); // console pinit (); // process (locks) binit (); // buffer cache fileinit (); // file table iinit (); // inode cache ideinit (); // ide (memory block device) timer_init (HZ); // the timer (ticker) sti (); userinit(); // first user process scheduler(); // start running processes
タイマーは10Hz駆動。stiでページのread/modify/writeを許可。userinitの中で、ユーザーの 役にたつプロセスが起動されるとな。後は、スケジューラーがそれらを監視して公平に 扱えるように務めるのか。
なおP2Vとかのマクロは、仮想/物理アドレスの変換用マクロ、memlayout.hに定義 されてる。
// Key addresses for address space layout (see kmap in vm.c for layout) #define KERNBASE 0x40000000 // First kernel virtual address // P:0x80000000 (PHY_START) => V:0xc0000000 #define V2P(a) (((uint) (a)) - KERNBASE) #define P2V(a) (((void *) (a)) + KERNBASE) #define V2P_WO(x) ((x) - KERNBASE) // same as V2P, but without casts #define P2V_WO(x) ((x) + KERNBASE) // same as V2P, but without casts
initの起動
userinitにBPを置いて、起動してみた。
(gdb) p *p $4 = { sz = 4096, pgdir = 0xc00dfc00, kstack = 0xc7fde000 "", state = RUNNABLE, pid = 1, parent = 0x0, tf = 0xc7fdefb8, context = 0xc7fdef88, chan = 0x0, killed = 0, ofile = {0x0 <repeats 16 times>}, cwd = 0xc00ac508 <icache+52>, name = "initcode\000\000\000\000\000\000\000" }
やがてinitcodeがinitになり、shを生む。コンソールには、
starting xv6 for ARM... hello man:) init: starting sh $
この時に呼ばれるのはusr/init.c
ずっとinitは生き続けて、forkしてshをexecしてる。zombieの検出もやってるのね。 shが死んだら、また生まれるように手配してるのか。