2014年8月8日 星期五

安裝 xv6

我從這本書得知 unix v6, 10000 行的 source code 是有可能看完的, 可是執行的平台卻是 pdp-11, 是不是要為了 unix v6 而去研究 pdp-11 這平台的 source code 實在令人猶豫, 我個人認為是划不來, 要花上的心力可想而知。

我雖然喜歡研究老東西, 不過也是有選擇的, 通常都是在這個時代還可以運用的技術。對於 cpu 我不想深入研究這已經退出舞台的機器。

需要做什麼準備工作呢? 我得去搞定 pdp 模擬器 simh (我可不想去弄台 pdp 機器, 家裡可沒大到擺這台), 可不是執行起來就夠, 還得能讓我 single step source code 才行。再來還有 pdp 的硬體架構, mmu, pre K&R c 語法, 研究 pdp-11 組合語言, 搞定 pdp-11 組譯器、老舊的 c 編譯器, 長長附錄 A 裡頭的參考資料可不是這麼容易就可以搞定的, 還沒開始看 code 就去半條命了。學到的 pdp 硬體架構也許有所幫助, 但以 cp 值來說實在是太低了, 這些東西都不好學, 要運用在目前流行的 x86/arm 還是要重頭學習一次, 能在目前流行的 x86/arm 學一次那就太好了。

xv6 就這麼出現了, 這是 mit 老師們為了教學和解除我上述的問題而在 x86 上開發的移植版本, 也大概是 10000 行左右 (組合語言和 c 語言), 我有了之前的學習, 應該可以看懂這 source code, 這比學習 linux 大怪物成功的機會大了不少, 和 linux 0.11 很類似。

安裝 xv6 到模擬器上

get source code
git clone git://pdos.csail.mit.edu/xv6/xv6.git
打個 make 很順利的產生需要的檔案。

在 qemu 執行 xv6
修改 Makefile

QEMU = qemu-system-i386
make qemu 

實際上執行的指令是:
qemu-system-i386 -serial mon:stdio -hdb fs.img xv6.img -smp 2 -m 512
執行訊息:

xv6...
cpu1: starting
cpu0: starting
init: starting sh
乖乖, 還真的支援多 cpu, 真的厲害, 當然, 拿掉 smp 就沒有 cpu1 了。

bochs 的話, 我沒有使用這個  dot-bochsrc, 我新增了一個可以在 bochs 2.6.6 使用的設定檔 bochsrc-266-conf。

/usr/local/bin/bochs -f  bochsrc-266-conf

我真羨慕能上這個課程的學生, 我只能自己辛苦打拚了。

我 clone 一份, 放在 github
git@github.com:descent/xv6.git

該從什麼地方開始研究呢? booting 好了, 從 Makefile 就可以看出端倪。

xv6.img: bootblock kernel fs.img
        dd if=/dev/zero of=xv6.img count=10000
        dd if=bootblock of=xv6.img conv=notrunc
        dd if=kernel of=xv6.img seek=1 conv=notrunc

由 qemu/bochs 指令可以得知需要 xv6.img, fs.img, 而 xv6.img 就是這樣產生的, 需要 bootblock。

bootblock: bootasm.S bootmain.c
        $(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c
        $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
        $(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o
        $(OBJDUMP) -S bootblock.o > bootblock.asm
        $(OBJCOPY) -S -O binary -j .text bootblock.o bootblock
        ./sign.pl bootblock

看到熟悉的 0x7c00, legacy bios booting 方式, 那 bootasm.S bootmain.c 就是開機的精華所在了。

sign.pl
 1 #!/usr/bin/perl
 2 
 3 open(SIG, $ARGV[0]) || die "open $ARGV[0]: $!";
 4 
 5 $n = sysread(SIG, $buf, 1000);
 6 
 7 if($n > 510){
 8   print STDERR "boot block too large: $n bytes (max 510)\n";
 9   exit 1;
10 }
11 
12 print STDERR "boot block is $n bytes (max 510)\n";
13 
14 $buf .= "\0" x (510-$n);
15 $buf .= "\x55\xAA";
16 
17 open(SIG, ">$ARGV[0]") || die "open >$ARGV[0]: $!";
18 print SIG $buf;
19 close SIG;

我看不懂 perl, 似乎只是加上 0x55aa。

bootmain.c
void readsect(void *dst, uint offset)
{
  // Issue command.
  waitdisk();
  outb(0x1F2, 1);   // count = 1
  outb(0x1F3, offset);
  outb(0x1F4, offset >> 8);
  outb(0x1F5, offset >> 16);
  outb(0x1F6, (offset >> 24) | 0xE0);
  outb(0x1F7, 0x20);  // cmd 0x20 - read sectors

  // Read data.
  waitdisk();
  insl(0x1F0, dst, SECTSIZE/4);
}

這是用 LBA mode 讀寫硬碟的程式碼, x86汇编语言:从实模式到保护模式 8.3.5 有詳細的說明, 這樣就不用 bios call, 可在 x86 protected mode 存取硬碟。不過不知道在真實機器上是不是可以正常執行?

ref:

readsect() 硬碟讀寫方式不使用 bios call:

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。