xv6

xv6

# アドバンスドソフトウェア2015ソース読み会 xv6

xv6 を読めるようにした vagrant box があるのでそれを使います。 中身は Ubuntu 14.04 です

# box のダウンロード

でダウンロードします。

# VM の起動

  • vagrant init xv6
  • vagrant up

で起動します。

# VM への ssh

  • vagrant ssh

で接続します。

# VM の内部構成

ホームディレクトリに xv6 と xv6-64 があります

両方とも動きますが今回は xv6-64 を使います。

# xv6 の起動

  • make qemu-nox

で起動します。

  • make qemu-nox-gdb

で gdb attach 待ちで起動します。 xv6 は単体で終了できないのでqemuを落とすことで終了します。(C-a x で qemu 終了)

# gdb の attach

xv6 は gdb 用の .gdbinit を自動で生成するようになっています。

ただ、 gdb は boot process の 16 -> 32 -> 64 bit の切り替えに対応していないので、切り替え次第違う gdb を用意して attach します。

  • gdb-multiarch -x .gdbinit64

とすると attach できます。これは16 -> 32 用の設定です。

attach している間にもう一つ

  • gdb-multiarch -x .gdbinit64-2

として 64 bit 用の gdb を上げます。

64 bit 用の gdb を上げたら 32bit 用の gdb で

  • continue

します。そうすると32bit用gdbが panic するので終了します。

32bit用gdb を終了すると 64bit用gdb がattachされているはずです。

なお、64bit用gdb の attach に失敗することが結構あります。

その時は64bit用gdbを落として $ gdb-multiarch -x .gdbinit64-2 をもう一度やると繋がります。

64bit用gdbが attach できればあとは main などに break をかけて読めます。

# boot process (16 -> 32)

まずは頭から読むことに

  • ブートは kernel/bootasm.S から始まります。
  • Intel CPU は 16bit mode(real-address mode) で始まるので、16bit の命令になってます。
  • アセンブラも .code16 で始まってます。
  • cli して ds,es, ss を zero fill してます。
  • その後に inb/outb で A20 を有効化。
  • 0xd1 と 0xdf を書き込んでます。
  • 0xd1 はこれから命令を書くよ、って命令で、0xdf は A20 を有効化します。
  • http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html
  • http://wiki.osdev.org/%228042%22_PS/2_Controller
  • lgdt で gdt をセット
  • gdt は global description table で address space の prefix table みたいなもんみたいです
  • gdt を切り替えることで仮想アドレスとかbitの拡張とかもするとか。
  • gdt 周りの図は Manual の Figure 2-2. System-Level Registers and Data Structures in IA-32e Mode とかです
  • http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
  • 最後に cr0 の PE を立てます。
  • これで Protected mode に入ります。
  • Protected mode は i386 の 32bit mode の名前です。
  • 16 から 32 に移る手順はマニュアルの Section 9-9 Mode Switcing に書かれてます。

# boot process (32 -> 64)

  • protected mode に入ったので 32bit 命令(.code32)
  • Register を初期化して call bootmain
  • bootmain は kernal/bootmain.c
  • bootmain は readseg して entry を取ってきてそれを実行する。
  • readseg は 0x10000 に命令を展開する
  • entry には header があって 0x1BADB002 ってマジックナンバーで識別してます
  • entry本体は kernel/entry64.S にあります
  • entry64 では page table を初期化してます
  • CR4.PAE を立てて memory space を 64bit に
  • EFER.LME を立てて 64bit命令に変更して
  • entry64low へ ljmp
  • これで64bit mode。ここから main に入ります

# main

  • 基本的には初期化
  • 全部読んだ訳じゃないので読んだところだけ
  • uartearlyinit
    • uart って chip の初期化。
    • console かな
  • kinit1
    • kernel 用のメモリの確保
    • memory 管理用の lock を作って freerange
    • freerange は kfree する
    • kfree したメモリ空間を list で確保
  • kvmalloc
    • kernel 用の page table を作る
  • acpiinit
    • acpi ってもので hardware information を取ってくるみたいです
    • 具体的には CPU の数
    • CPU が複数あれば mpinit も呼ぶ
  • seginit
    • gdt とかの設定
  • pinit
    • process table 用の lock を作っておわり

# syscall

次に syscall を読もうということに

  • kernel/syscall.c にあります
  • syscall table は include/syscall.h にあります
  • syscall は trap から呼ばれるみたいですね
  • trap は alltraps から呼ばれます
  • alltraps は kernel/alltraps.S にあって trap vector を設定してます

# sys_wait

具体的な syscall としてまずは sys_wait

  • 本体は kernel/proc.c の wait()
  • ptable を lock
  • 子プロセスが残ってないかチェックして
  • sleep()
  • sleep は proc の state を SLEEPING にして sched()
  • sched は interuppt が enable かどうかチェックして swtch()
  • swtch は stack pointer を切り替えます
  • これで実行されるプロセスが切り替えられる

# scheduler

sched を読んだので scheduler 本体を読む

  • scheduler() は kernel/proc.c
  • proc って変数があってこれは per CPU variable
  • gcc では __thread が付いた thread local storage
  • https://gcc.gnu.org/onlinedocs/gcc-3.4.1/gcc/Thread-Local.html
  • 内部的には fs って CPU ごとに違う値を持った register をかますみたいです
  • scheduler は ptable を見て RUNNABLE なものに swtch
  • swtch するまえに swtchuvm で user virtual memory を切り替えてます

# sys_fork

あと1つくらい syscall を読もう、ということで fork

  • fork() は kernel/proc.c
  • allocproc で ptable の空きを取ってきて
  • copyuvm で memory をcopy
  • copy on write はたぶんしてない

# x86のdebug

qemu

  • /mnt/dalmore-home/one/src/xv6-rpi/src

  • 最初にqemuを予め立ち上げる

  • CtrlA-Xで脱出

  • cd /mnt/dalmore-home/one/src/xv6-rpi/src

  • /net/open/Linux/arm/gcc-arm-none-eabi-7-2017-q4-major/bin/arm-none-eabi-gdb kernel.elf

  • 最初にqemuを予め立ち上げる

  • (gdb) b _start

  • (gdb) target remote :1234

  • qemu再起動

1
make clean;make -f makefile-armclang qemu-debug

# ソフトウェアシステム論xv6読み会

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy