GSoC 2012 week6:INT命令をハンドルしてみる
まずブートセクタをゲスト空間に読み込めるようにする。
disk_fd = open(disk_image, O_RDONLY); if (read(disk_fd, &membase[0x7c00], 512) != 512) { perror("read "); return (1); }
これに合わせて、ゲストのRIPは0x7c00から開始するように設定しておいた。(コード省略)
次に、リアルモードの割込みハンドラだが、こんな風にしてみた。
addr = 0x400; for (i = 0x0; i < 0x400; i += 0x4) { uint16_t *p = (uint16_t *)&membase[i]; membase[addr + 0] = 0x0f; /* vmcall(3byte) */ membase[addr + 1] = 0x01; membase[addr + 2] = 0xc1; membase[addr + 3] = 0xcf; /* iret */ *p = addr; p = (uint16_t *)&membase[i + 0x2]; *p = 0x0; addr += 4; }
マジックナンバーじみてて分かりにくいコードだが、割り込み番号0x0〜0xffの割り込みハンドラを0x400から0x4刻みで一対一に用意していて、ハンドラの中身はvmcall; iret;となっている。
ゲスト内の割り込みハンドラで実行されたvmcallで生じるVMExitをハンドルするコードに、レジスタダンプとRIPから計算出来る割り込み番号の表示を入れてみる。
fbsdrun.c
error = vm_get_register(ctx, *pvcpu, VM_REG_GUEST_RSP, &rsp); if (!error) error = vm_get_register(ctx, *pvcpu, VM_REG_GUEST_RIP, &rip); if (!error) error = vm_get_register(ctx, *pvcpu, VM_REG_GUEST_RAX, &rax); if (!error) error = vm_get_register(ctx, *pvcpu, VM_REG_GUEST_RBX, &rbx); if (!error) error = vm_get_register(ctx, *pvcpu, VM_REG_GUEST_RCX, &rcx); if (!error) error = vm_get_register(ctx, *pvcpu, VM_REG_GUEST_RDX, &rdx); if (error) { printf("errno = %d\n", errno); return (VMEXIT_ABORT); } printf("VMCALL handled\n"); printf("rsp=%"PRIx64" rip=%"PRIx64" rax=%"PRIx64" rbx=%"PRIx64" rcx=%"PRIx64" rdx=%"PRIx64"\n", rsp, rip, rax, rbx, rcx, rdx); intr = (rip - 0x400) / 0x4; printf("intr=%"PRIu64"\n", intr); return (VMEXIT_ABORT);
こんな感じ。今のところ、割り込みから復帰させずに実行を中断している。
では試しに、こんなブートセクタプログラムをロードして、正しい値が得られるか試してみる。
testbootsect.S
.code16 mov $0x1, %ax mov $0x2, %bx mov $0x3, %cx mov $0x4, %dx int $0x13 int $0x14
以下が実行結果。
$ sudo bhyvebiosload -d ~/testbootsect/testbootsect.bin -m 128 -M 256 vm0 $ sudo bhyve -b -m 128 -M 256 vm0 VMCALL handled rsp=7ff8 rip=44c rax=1 rbx=2 rcx=3 rdx=4 intr=19
レジスタ値と割り込み番号が正しく取れている事が分かる(※0x13 == 19)。
今週は全くカーネル弄ってない。
というか、実の所、ここからの作業はほぼ100%ユーザランドなのだ。
詳しくは
svn diff -r238305:238881 https://socsvn.freebsd.org/socsvn/soc2012/syuu/bhyve-bios