使用
VirtualPC & Hyper-V
-
- 於虛擬機內安裝 openssh-server,即可在 Windows 內用 putty 連線至虛擬機。用 ifconfig 確認虛擬機網址。
-
-
VirtualBox
$ svn co http://www.virtualbox.org/svn/vbox/trunk vbox $ cd vbox $ ./configure --disable-hardening
-
-
- 在 VirtualBox 上讀取底下 Windows 的 SD 卡。
- 用ssh或http連線進Virtualbox的虛擬電腦
cd "c:\Program Files\Oracle\VirtualBox" VBoxManage modifyvm vm_name --natpf1 "ssh,tcp,,2222,,22"
- Ubuntu 要先裝 ssh。
-
- 共享目錄
-
$ sudo usermod -aG vboxsf $(whoami)
LXC
$ sudo apt-get install vnc4server
-
- 不建議使用 lxc 運行 GUI 改用 KVM
QEMU
建置 QEMU
QEMU 1.0 預設會編譯成 PIE,這對舊版的 GDB 會有影響。強制開啟 IO thread。使用 Clang 編譯 QEMU 會出現以下訊息1)2)3),現在已能用 Clang 編譯。
In file included from /z/tmp/chenwj/qemu-1.0/user-exec.c:21: /z/tmp/chenwj/qemu-1.0/dyngen-exec.h:64:20: error: global register variables are not supported register CPUState *env asm(AREG0); ^ 1 warning and 1 error generated.
TCI 沒有用到 global register variable。
$ ../qemu-1.0/configure --target-list=i386-bsd-user --enable-tcg-interpreter --cc=clang \ --extra-cflags="-v" --disable-smartcard-nss
User Mode
目前 QEMU 將 glib2.0 列為必要,The GTK+ Project。4)
- 如果系統沒有安裝 glib,則自行安裝。
$ apt-get install zlib1g-dev pkg-config libglib2.0-dev $ wget http://ftp.gnome.org/pub/gnome/sources/glib/2.28/glib-2.28.0.tar.gz; tar xvf glib-2.28.0.tar.gz $ mkdir build; cd build $ ../glib-2.28.0/configure --prefix=$INSTALL $ make install
- 下載 QEMU。
# http://wiki.qemu.org/download/qemu-0.15.1.tar.gz $ git clone git://git.qemu.org/qemu.git
- 修改 configure。
# glib support probe #if $pkg_config --modversion gthread-2.0 > /dev/null 2>&1 ; then # 用 pkg-config 查詢 glib_cflags="-pthread -I$INSTALL/include/glib-2.0/ -I$INSTALL/lib/glib-2.0/include/" glib_libs="-L$INSTALL/lib -pthread -lgthread-2.0 -lrt -lglib-2.0" LIBS="$glib_libs $LIBS" libs_qga="$glib_libs $libs_qga" #else # echo "glib-2.0 required to compile QEMU" # exit 1 #fi
- 編譯並安裝 QEMU。
# config.log 可以用來檢查設定 QEMU 時哪裡出錯 $ mkdir build; cd build $ ../qemu/configure --prefix=$INSTALL --target-list=i386-linux-user --enable-debug $ make; make install
- 如果 glib 是自行安裝。
$ export LD_LIBRARY_PATH=$INSTALL/lib/ # -m32 會編譯 32-bit 執行檔 $ gcc -m32 hello.c -o hello $ qemu-i386 hello
SPARC
關於 SPARC V8 (32-bit)、V8PLUS (64-bit with 32-bit ABI) 和 V9 (64-bit) 架構的差異請見 ABI Compliance and Global Registers Usage in SPARC V8 and V9 Architecture。QEMU 對 v9 支援最完整 5)。64-bit with 32-bit ABI 和在 x86_64 上運行 x86 binary 不一樣。64-bit with 32-bit ABI 代表程式可以存取 64-bit 硬體資源,但其內存空間仍只侷限在 32-bit; x86 binary 運行在 x86_64 上不能存取 64-bit 硬體資源 6)。
$ uname -a Linux sparc 2.6.37-rc5-git #1 SMP Tue Dec 21 17:03:53 CST 2010 sparc64 sun4v UltraSparc T2 (Niagara2) GNU/Linux $ file /bin/ls /bin/ls: ELF 32-bit MSB executable, SPARC32PLUS, V8+ Required, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, stripped # 雖然機器是 64-bit,但是裝的是 32-bit OS # --sparc_cpu=v9 改用 sparc64-linux-gcc $ configure --prefix=$INSTALL --target-list=i386-linux-user --sparc_cpu=v8plus
PowerPC
因為 PS3 記憶體不足,可能導致 GCC 編譯 QEMU 失敗 7)8)。改以 debug 模式編譯。
- 註解 assert。
--- qemu-0.13.0/tcg/tcg.c.orig 2010-10-16 04:56:09.000000000 +0800 +++ qemu-0.13.0/tcg/tcg.c 2011-02-24 09:46:28.796899001 +0800 @@ -1031,7 +1031,7 @@ def = &tcg_op_defs[op]; #if defined(CONFIG_DEBUG_TCG) /* Duplicate entry in op definitions? */ - assert(!def->used); + //assert(!def->used); def->used = 1; #endif nb_args = def->nb_iargs + def->nb_oargs;
- 以 debug 模式編譯,或是參考 http://cruxppc.org/viewvc/opt/branches/2.7/qemu/Pkgfile?revision=2267&view=markup9)。
$ configure --prefix=$INSTALL --target-list=i386-linux-user --enable-debug $ make install
- 拷貝 x86 函式庫供 powerpc 上的 qemu-i386 使用。函式庫必須放在以 lib 為名的目錄底下。
$ mkdir $HOME/tools/lib $ cp /lib32/ld-linux.so.2 $HOME/tools/lib $ cp /lib32/libc.so.6 $HOME/tools/lib $ export LD_LIBRARY_PATH=$HOME/tools/lib $ qemu-i386 -L $HOME/tools hello
ARM
- 安裝交叉工具鏈。使用預編譯好的工具鏈。可以到 Sourcery G++ Lite Edition 下載。或者使用 crosstool-NG 建立工具鏈。
- 下載並安裝 crosstool-ng。
# 預設安裝在 $HOME/x-tools 下。 # 注意運行 QEMU 平台的內核,這裡要選較其為舊的內核建立工具鏈。 # 請記得將 crosstool.config 更名為 .config。 $ wget http://ymorin.is-a-geek.org/download/crosstool-ng/crosstool-ng-1.9.2.tar.bz2; tar xvf crosstool-ng-1.9.2.tar.bz2 $ cd crosstool-ng-1.9.2; $ ./configure --prefix=$INSTALL $ make install
- 建置交叉工具鏈。
$ mkdir toolchain-build $ cp $INSTALL/lib/ct-ng-1.9.2/samples/arm-unknown-linux-gnueabi/* . $ mv crosstool.config .config $ ct-ng menuconfig $ ct-ng build
- 安裝運行時期函式庫。如果要安裝預編譯好的運行時期函式庫。可以到 linux-arm.org 下載。
# mount cramfs 要有 root 權限。可以改採下面的作法,使用 fakeroot-ng。 $ wget http://www.linux-arm.org/git?p=ael.git;a=blob_plain;f=filesystem/bin/alip-ael-armv6-full-reduced.cramfs;hb=2010q4 $ fakeroot-ng # /sbin/cramfsck filesystem_bin_alip-ael-armv6-full-reduced.cramfs -x runtime/ # cp runtime/lib/* /path/to/runtime/lib # exit $
也可以使用 crosstool-NG,在建立工具鏈時同時也會得到運行時期函式庫。預設安裝在 $HOME/x-tools/arm-unknown-linux-gnueabi/arm-unknown-linux-gnueabi/sys-root/lib。PowerPC64 需要為 sys-root/lib64 和 sys-root/usr/lib64 建立軟鏈結,請 chmod u+w sys-root 和 chmod u+w sys-root/usr。
- 執行 QEMU。假設 runtime 裝在 /path/to/runtime/lib。
$ qemu-arm -L /path/to/runtime hello $ qemu-ppc64 $HOME/x-tools/powerpc64-unknown-linux-gnu/powerpc64-unknown-linux-gnu/sys-root hello
System Mode
- 編譯 QEMU。
$ wget http://download.savannah.gnu.org/releases/qemu/qemu-0.14.1.tar.gz; tar xvf qemu-0.14.1.tar.gz $ mkdir build; cd build # i386-softmmu 執行檔名稱為 qemu。將在 QEMU 1.0 修正。 $ ../qemu-0.14.1 --prefix=$INSTALL --target-list=i386-softmmu,arm-softmmu $ make && make install
- 編譯內核映像。
- Lab 2 編譯 kernel
# 取得 arch/i386/boot/bzImage 和 vmlinux 兩個文件 # kernel hacking 開啟 debug $ wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.20.tar.gz; tar xvf linux-2.6.20.tar.gz # http://blog.csdn.net/livingpark/article/details/3732679 # 在 scripts/mod/sumversion.c 加上 #include <limits.h> $ cd linux-2.6.20 # make mrproper 清理原始檔和設定 $ make mrproper; make ARCH=i386 menuconfig; make ARCH=i386
- 準備 root file system。如果沒有 root 權限的話,可以安裝 fakeroot 或是 fakeroot-ng,或是用 buildroot。
- Create root filesystem step by step
$ wget http://busybox.net/downloads/busybox-1.18.5.tar.bz2; tar xvf busybox-1.18.5.tar.bz2 $ cd busybox-1.18.5 # Enable Busybox setting/Build Options/Build busybox as static binary $ make menuconfig $ make; make install $ cd ..; fakeroot-ng; # mkdir root; cd root # mkdir dev bin # mknod dev/console c 5 1 # mknod dev/tty2 c 4 2 # mknod dev/tty3 c 4 3 # mknod dev/tty4 c 4 4 # cp ../busybox-1.18.5/busybox bin # ln -s bin/busybox init # cd bin # ln -s busybox sh # cd .. # find . | cpio -o -H newc | gzip > ../initramfs
或是直接使用 QEMU 官網上的測試檔。
$ wget http://wiki.qemu.org/download/arm-test-0.2.tar.gz; tar xvf arm-test-0.2.tar.gz $ cd arm-test # zImage.integrator 是內核文件。arm_root.img 是根文件系统。 # gzip -dc arm_root.img | cpio -idv $ qemu-system-arm -kernel zImage.integrator -initrd arm_root.img -nographic -append "console=ttyAMA0" $ wget http://wiki.qemu.org/download/minix204.tar.bz2; tar xvf minix204.tar.bz2 $ cd minix204 $ qemu -m 8M -fda vfloppya.img -hda minix204.img -boot c -vnc 0.0.0.0:1 $ wget http://wiki.qemu.org/download/linux-0.2.img.bz2 $ bunzip2 linux-0.2.img.bz2 # 使用 vncviwer 連至 server:1 即可得到輸出。 # 使用非常類似的 2.6.20 config。 # http://bellard.org/jslinux/linuxstart-20110820.tar.gz #qemu -S -kernel bzImage -hda linux-0.2.img -append "root=/dev/hda" -vnc 0.0.0.0:1 $ qemu linux-0.2.img -vnc 0.0.0.0:1
- -kernel <kernel image> : -kernel 参数后面接内核映像文件名,如果不指定路径,则在当前目录下寻找。-kernel bzImage 表示指定 bzImage 作为内核镜像文件。
- -initrd <initial ram disk image> : 需要为内核加在的初始 RAM 磁盘,里面包含文件系统。
- -hda linux-0.2.img 表示 linux-0.2.img 中的文件系统都放在 hda 中。
- -nographic : 由于这里仿真的目标板没有图形设备,所以我们也没必要让 QEMU 仿真图形设备,所以这里使用此参数。
- -append <parameters> : 一般的,我们在启动 linux 时,我们有时需要给内核传递一些参数,比如 init=/etc/init 指定内核启动后,最后将从运行位于 /etc 目录下的 init 文件 (该文件默认位于 /sbin/ 目录下)。这里,我们设置 console=ttyAMA0 表示内核使用 /dev/ttyAMA0 设备作为控制台,该设备由 QEMU 创建,这样目标板就有了一个作为终端的伪串行设备。-append "root=/dev/hda" :-append 后接命令行参数,这里表示根文件系统设备使用 hda 。
退出 QEMU 操作为:Ctrl+Alt ,然后按下 X 键。注意,它不会提醒你是否要退出,而是直接退出,所以操作时要小心。
-
- -S 参数表示在启动时CPU 停止(见上图的标题栏中的 stopped) 并等待 GDB 连接。
QEMU 的 LInux 映像檔裡包含 NBench,分數越高越好。要切換到 nbench 的目錄底下執行 nbench 這隻測試程式,否則會噴出以下訊息:
CPU:NNET--error in opening file!
-
- initrd 和 initramfs 是不同的東西,前者以 disk 為導向; 後者以 mem 為導向。
製作 QEMU 官網上的硬盤映像。
- Create the RAM disk whose size is 64M.
$ dd if=/dev/zero of=disk.img bs=4096 count=16384
- Partition the disk.
$ /sbin/fdisk -C 16065 -H 255 -S 63 disk.img
- Format the filesystem.
- Copy file system.
- Build the bootloader by grub.
- Boot system with your new disk.
-
-
- 感謝作者的回信。
shutdown 和 reboot 不會導致重開機,exit 登出 shell 會導致重開機。linux-0.11 登出後不會重開機10)。
# -no-shutdown -no-reboot: 登出後,出現 "machine restart" 之後停住。 (qemu) system_powerdown 無反應 (qemu) system_reset QEMU 停止運作 # -no-reboot: 登出後,離開 QEMU。(exit instead of rebooting) (qemu) system_powerdown 無反應 (qemu) system_reset 關閉 QEMU # -no-shutdown: 登出後,重啟系統。(stop before shutdown) (qemu) system_powerdown 無反應 (qemu) system_reset 重啟系統 # 不加 -no-shutdown -no-reboot (qemu) system_powerdown 無反應 (qemu) system_reset 重啟系統
安裝系統
$ wget http://releases.ubuntu.com/12.04.1/ubuntu-12.04.1-server-amd64.iso $ qemu-img create -f qcow2 ubuntu_x86_64.qcow2 3G $ qemu-system-x86_64 -m 1024M -boot d -cdrom ubuntu-12.04.1-server-amd64.iso \ -hda ubuntu_x86_64.qcow2 -vnc 0.0.0.0:1 $ qemu-system-x86_64 -m 1024M -hda ubuntu_x86_64.qcow2 -vnc 0.0.0.0:1
# 使用預安裝的映像檔。 $ wget http://people.debian.org/~aurel32/qemu/armel/vmlinuz-2.6.32-5-versatile $ wget http://people.debian.org/~aurel32/qemu/armel/initrd.img-2.6.32-5-versatile $ wget http://people.debian.org/~aurel32/qemu/armel/debian_squeeze_armel_desktop.qcow2 $ qemu-system-arm -M versatilepb -kernel vmlinuz-2.6.32-5-versatile \ -initrd initrd.img-2.6.32-5-versatile \ -hda debian_squeeze_armel_desktop.qcow2 -append "root=/dev/sda1" -vnc 0.0.0.0:1 # 請到 http://ftp.de.debian.org/debian/dists/ 找類似 Debian6.0.4/main/installer-armel/current/images/versatile/netboot # 的路徑,下載 initrd.gz。 $ qemu-img create -f qcow2 hda.img 3G $ qemu-system-arm -M versatilepb \ -kernel vmlinuz-2.6.32-5-versatile -initrd initrd.gz \ -hda hda.img -append "root=/dev/ram" -vnc 0.0.0.0:1 # 安裝完後,關閉 QEMU。並改用底下指令開機。 $ qemu-system-arm -M versatilepb \ -initrd initrd.img-2.6.32-5-versatile \ -hda hda.img -append "root=/dev/sda1" -vnc 0.0.0.0:1 # 或是改從 console 輸出。 $ qemu-system-arm -M versatilepb \ -initrd initrd.img-2.6.32-5-versatile \ -hda hda.img -append "root=/dev/sda1 console=ttyAMA0" -nographic # 下載 linux kernel tarball,解開後在 arch/arm/configs 找 config。 $ wget 'http://linux-arm.org/git?p=ael.git;a=blob_plain;f=kernel/config/config-ael-2011.06.00-realview-v7-smp-thumb;hb=2011.06'
-
-
-
- 對於 ARM 而言,不同的板子有不同的設定。又如果板子內存有上限,無法透過
-m xxxM
來突破該限制11)。
Buildroot
# http://buildroot.uclibc.org/downloads/snapshots/buildroot-snapshot.tar.bz2 $ wget http://buildroot.uclibc.org/downloads/buildroot-2011.11.tar.gz; tar vxf buildroot-2011.11.tar.gz $ cd buildroot-2011.11 # for kernel,請見 arch/i386/defconfig 或 arch/arm/configs。make help 可以看到更多資訊。 # 先產生 qemu 用的 default config。 $ make qemu_x86_defconfig # 設定 linux kernel version。最後生成的 rfs 格式,initramfs。 # 注意編譯 kernel 時所用的 config 從哪裡來。 # # - gzip, tar # # - rpm # # - dropbear: openssh 會一直要求改密碼。 # $ make menuconfig $ make # 使用 QEMU 內部的 DHCP 以便存取網路。 $ vi output/target/etc/network/interfaces auto eth0 iface eth0 inet dhcp $ cd output/images/ # 建議先轉成 qcow2 以便動態增長硬盤大小。rootfs.qcow2 只能增長成原本硬盤映像的大小。 # 加大或減小硬盤大小之後或之前,必須重新在 VM 裡重切分割區。 $ qemu-img convert -O qcow2 rootfs.ext2 rootfs.qcow2 $ qemu-img resize rootfs.qcow2 +3G $ qemu-img info rootfs.qcow2 image: rootfs.qcow2 file format: qcow2 virtual size: 3.2G (3399145472 bytes) disk size: 159M cluster_size: 65536 $ qemu-system-i386 -kernel bzImage -initrd rootfs.cpio -vnc 0.0.0.0:1 $ qemu-system-i386 -kernel bzImage -hda rootfs.ext2 -append "root=/dev/sda console=ttyAMA0" -nographic \ -redir tcp:2222::22 \ -redir tcp:3333::3333 # 登入後,修改密碼。 $ passwd new_passwd # 修改密碼若失敗,請修改 /etc/sshd_config,在其中將 UsePrivilegeSeparation 開啟: # UsePrivilegeSeparation yes $ ssh -p 2222 root@localhost
- 有些套件需要 wchar 支援。12)
# 欲增加 3G 硬盤空間。 $ qemu-img create -f raw additional.raw 3G # 先將已存在的硬盤映像轉成 raw 格式。 $ qemu-img convert -f qcow2 example.img -O raw example.raw $ cat additional.raw >> example.raw
$ -serial mon:telnet::4444,server,nowait # VM 裡要開啟 telnet server,telnetd。才可以在 host 下 telnet 連進 VM。 $ telnet 127.0.0.1 4444
Monitor
# 有時候會遇到 vnc 無法開啟的情況,使用 screendump 輸出螢幕內容。 # 再用 http://www.online-convert.com 轉成 jpeg 觀看。 (qemu) screendump screenshot.ppm
(qemu) info jit Translation buffer state: gen code size 3785504/4089856 TB count 11244/32768 TB avg target size 14 max=595 bytes TB avg host size 336 bytes (expansion ratio: 23.4) cross page TB count 25 (0%) // guest TB 對映的 guest binary 跨 guest page 的個數和比例。 direct jump count 7569 (67%) (2 jumps=5173 46%) Statistics: TB flush count 0 TB invalidate count 2737 TLB flush count 13869
# 印出客戶機虛擬位址處的內容。 (qemu) x /2 0x08d31008 08d31008: 0x64636261 0x00006665 # 印出客戶機物理位址處的內容。 (qemu) xp /2 0x01603008 0000000001603008: 0x64636261 0x00006665 # 顯示內存、MMIO、IO 地址分佈。 # 以縮排顯示階層關係。 (qemu) info mtree I/O 0000000000000000-000000000000ffff (prio 0): io 0000000000000020-0000000000000021 (prio 0): pic
使用 Ctrl+Alt+2 组合键切换到 QEMU 终端 (QEMU Monitor),然后输入 gdbserver ,启动 gdbserver 服务。 这时启动另外一个终端窗口,输入 gdb vmlinux 命令进行调试:
(gdb) target remote localhost:1234 /*使用 gdbserver 进行调试命令*/
或是將 QEMU 終端導至標準輸出。
$ qemu -hda linux-0.2.img -vnc 0.0.0.0:1 -monitor stdio
Snapshot
- 先確定裝置格式支援 snapshot。raw 不支援 snapshot。Monitor。
(qemu) info block ide0-hd0: removable=0 io-status=ok file=linux-0.2.img ro=0 drv=raw encrypted=0 ide1-cd0: removable=1 locked=0 tray-open=0 io-status=ok [not inserted] floppy0: removable=1 locked=0 tray-open=0 [not inserted] sd0: removable=1 locked=0 tray-open=0 [not inserted] (qemu) savevm Device 'ide0-hd0' is writable but does not support snapshots.
- 用 qemu-img 轉換硬盤映像成 qcow2 格式。
$ qemu-img convert -O qcow2 linux-0.2.img linux-0.2.qcow2
- 在 monitor 在下 savevm 把 RAM、device state 和 disk 存到當前使用的硬盤映像13)。
(qemu) savevm (qemu) loadvm (qemu) info snapshots
Network
QEMU 可以透過虛擬網卡連至 vlan,vlan 之間可以透過 slirp,socket,tap 或是 vde 連接。在 Documentation/Networking 提到的 virtual network device 即為虛擬網卡,network backend 負責將虛擬網卡送出的資料放到真實網路上,可以是 slirp,socket,tap 或是 vde。QEMU Networking 裡面的 usermode network stack 即為 slirp,slirp 為預設網路後端,見下圖。 QEMU 內部會啟動一個 DHCP 伺服器,宿主作業系統不可見。
- 存取宿主機的資料。
# 在 host 執行底下命令。 $ cd path/to/shared/files && python -m SimpleHTTPServer # 在 guest 執行底下命令。 $ wget http://10.0.2.2:8000/README
- 存取客戶機的資料。
# 將 host TCP 連線 127.0.0.1:8000 導到 guest 內部 DHCP 預設 IP 埠 8000。 $ qemu-system-x86_64 -m 1024M -hda ubuntu_x86_64.qcow2 \ -net user,hostfwd=tcp:127.0.0.1:8000-:8000 \ -vnc 0.0.0.0:1 $ wget localhost:8000/README
$ qemu-system-i386 -kernel bzImage-x86 -hda disk-x86.raw -append "root=/dev/sda" \ -net nic -net tap,ifname=tap0,script=no -vnc :3 -monitor stdio
Tracing
請見 docs/tracing.txt。
$ wget http://download.savannah.gnu.org/releases/qemu/qemu-0.14.0.tar.gz $ tar xvf qemu-0.14.0.tar.gz $ vim qemu-0.14.0/trace-events # qemu-malloc.c qemu_malloc(size_t size, void *ptr) "size %zu ptr %p" qemu_realloc(void *ptr, size_t size, void *newptr) "ptr %p size %zu newptr %p" qemu_free(void *ptr) "ptr %p" $ make install build; cd build; $ ../qemu-0.14.0/configure --prefix=/path/to/install --target-list=i386-linux-user --enable-trace-backend=stderr $ ./path/to/install/qemu-i386 hello
Cross Compile
- 需要 zlib 的 ARM binary。
$ wget http://zlib.net/zlib-1.2.5.tar.gz; tar xvf zlib-1.2.5.tar.gz # export PATH=/PATH/TO/CROSSTOOL:$PATH $ CC=arm-none-linux-gnueabi-gcc ./configure \ --prefix=$INSTALL
- 執行底下腳本。
#!/bin/bash SOURCE_DIR=../user-mode ZLIB_PREBUILT=$HOME/x-tools TARGET_LIST=arm-linux-user CROSS_PREFIX=arm-none-linux-gnueabi- CPU=armv7l CFLAGS=-I$ZLIB_PREBUILT/include LDFLAGS=-L$ZLIB_PREBUILT/lib PREFIX=`pwd` OPT_FLAGS="--disable-sdl" DEBUG=--disable-strip STATIC=--static $SOURCE_DIR/configure --target-list=$TARGET_LIST \ --cpu=$CPU \ --extra-ldflags=$LDFLAGS \ --extra-cflags=$CFLAGS \ --cross-prefix=$CROSS_PREFIX \ --prefix=$PREFIX
Debug & Testing
$ git clone git://git.qemu.org/qemu-test.git $ cd qemu-test $ git submodule update --init $ make
舊版 GDB 無法處理 PIE。
$ ../qemu-1.0/configure --prefix=$INSTALL --target-list=i386-softmmu --enable-debug --disable-pie
(gdb) handle SIG38 noprint pass # 定位感興趣的 guest pc 以便查找其在 code cache 中的位址。 (gdb) b gen_intermediate_code_internal if tb->pc == 0x801a3bc # 此時已生成 host binary,查找 qemu.log。 (gdb) b tb_find_fast
QEMU 內部會註冊 SIGNAL 供自己使用,例如 IO thread 執行完畢之後會發送 SIGNAL 給 vcpu thread。
- 在 qemu_tcg_init_cpu_signals (cpus.c) 註冊 QEMU 內部使用的 SIGNAL。
static void qemu_tcg_init_cpu_signals(void) { sigset_t set; struct sigaction sigact; memset(&sigact, 0, sizeof(sigact)); sigact.sa_handler = cpu_signal; sigaction(SIG_IPI, &sigact, NULL); sigemptyset(&set); sigaddset(&set, SIG_IPI); pthread_sigmask(SIG_UNBLOCK, &set, NULL); }
- cpu_signal 會將執行從 code cache 拉出來,回到 QEMU 處理 singal。
static void cpu_signal(int sig) { if (cpu_single_env) { cpu_exit(cpu_single_env); } exit_request = 1; }
分層
依照 A Survey on Virtualization Technologies,虛擬化可分成如下幾層 (由上至下):
-
-
-
- FreeBSD jail 屬於此例。
-
- 位於 OS 最下層,位於硬體之上。VMWare 或 Xen 屬於此例,guest 和 host 屬於同一個 ISA。
A hardware abstraction layer (HAL) is an abstraction layer, implemented in software, between the physical hardware of a computer and the software that runs on that computer. Its function is to hide differences in hardware from most of the operating system kernel, so that most of the kernel-mode code does not need to be changed to run on systems with different hardware.
-
- QEMU 屬於此例,guest 和 host 可不屬於同一個 ISA。
An instruction set, or instruction set architecture (ISA), is the part of the computer architecture related to programming, including the native data types, instructions, registers, addressing modes, memory architecture, interrupt and exception handling, and external I/O. An ISA includes a specification of the set of opcodes (machine language), and the native commands implemented by a particular processor.
Linux® emulation in FreeBSD
- 透過設置不同的向量表,FreeBSD 可以同時運行 FreeBSD 和 Linux 執行檔。
i386/i386/elf_machdep.c
和i386/linux/linux_sysvec.c
分別定義不同的向量表。struct sysentvec elf32_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_mask = 0, .sv_sigsize = 0, .sv_sigtbl = NULL, .sv_errsize = 0, .sv_errtbl = NULL, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = sigcode, .sv_szsigcode = &szsigcode, .sv_prepsyscall = NULL, .sv_name = "FreeBSD ELF32", .sv_coredump = __elfN(coredump), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_pagesize = PAGE_SIZE, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_stackprot = VM_PROT_ALL, .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_IA32 | SV_ILP32, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, }; struct sysentvec elf_linux_sysvec = { .sv_size = LINUX_SYS_MAXSYSCALL, .sv_table = linux_sysent, .sv_mask = 0, .sv_sigsize = LINUX_SIGTBLSZ, .sv_sigtbl = bsd_to_linux_signal, .sv_errsize = ELAST + 1, .sv_errtbl = bsd_to_linux_errno, .sv_transtrap = translate_traps, .sv_fixup = elf_linux_fixup, .sv_sendsig = linux_sendsig, .sv_sigcode = linux_sigcode, .sv_szsigcode = &linux_szsigcode, .sv_prepsyscall = NULL, .sv_name = "Linux ELF", .sv_coredump = elf32_coredump, .sv_imgact_try = exec_linux_imgact_try, .sv_minsigstksz = LINUX_MINSIGSTKSZ, .sv_pagesize = PAGE_SIZE, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_stackprot = VM_PROT_ALL, .sv_copyout_strings = linux_copyout_strings, .sv_setregs = exec_linux_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_LINUX | SV_IA32 | SV_ILP32, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = linux_fetch_syscall_args, .sv_syscallnames = NULL, };
proc.h
裡面定義的proc
資料結構內存該進程應該使用哪一個sysentvec
。struct proc { struct sysentvec *p_sysent; /* (b) Syscall dispatch info. */ };
術語
- Architected State
- ISA 會定義出指令可操作和存取的資源,一般包含暫存器、記憶體。
- Block Linking
- Indirect Branch Translation Cache (IBTC)
- Shadow Stack
- Precise Exception
- 當一條指令發生例外,在其前的指令必須要完成,在其後的指令不可完成。
- Virtual Machines: Versatile Platforms for Systems and Processes 3.6 (p.119)、4.5 (p.186)、7.4 (p.345)
- Translation lookaside buffer TLB miss handling 一節有描述 architected page table (hardware TLB management) 和 architected TLB (software-managed TLB)。
- Architected Page Table
- ISA 定義頁表結構,這代表當發生 TLB 缺失時,硬體會查詢頁表 (因為其結構已知),並將頁表項填入 TLB。換言之,整個過程中,TLB 對作業系統而言不可見。當硬體查詢頁表,發生頁缺失時,這時才會陷入作業系統。
- Architected TLB
- ISA 定義 TLB 的結構和操作 TLB 的指令,將頁表的實作留給作業系統,硬體並不知道頁表的存在,由作業系統負責將頁表項寫入 TLB。
- 上述代表當 TLB 缺失時,會陷入作業系統,由作業系統負責將頁表項寫入 TLB。
-
- 著墨在不同 OS 系統呼叫,而非不同 ISA 的轉換。
$ wget ftp://ftp.freebsd.org/pub/FreeBSD/releases/i386/i386/9.0-RELEASE/src.txz; tar vxf src.txz # usr/src/sys/modules/linux $ cd usr/src/sys/compat/linux
論文
文章
外部連結
-
- source 表客戶,target 表宿主。