kvm api でいろいろするやつ ============================== 参考: 公式のAPIに関してのドキュメント: https://www.kernel.org/doc/html/latest/virt/kvm/api.html https://github.com/torvalds/linux/blob/2861952/Documentation/virtual/kvm/api.txt kvmを使ってみるやつ: http://yuma.ohgami.jp/Identeki-MBR/01_hello.html 初歩的な機能を出力だけのROMを用意してやってみる ------------------------------------------------ :: ### とりあえず/dev/kvmをopenする. 当然kvm的な操作はここにioctlすることになる. int kvmfd = open("/dev/kvm", O_RDWR); ### VMを作成する.(VMのメモリのやつとかそこらへんができるんだと思う.←←←調べ直して書き直す) int vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0); ### ROMを登録する.要はここにBIOSとかぶっ込んで(これハードディスクはどうすればいいのかわからんけど.別で同じように登録して良いんか) ### 起動すればいいという話 ### とりあえずテスト用に雑なバイナリをくっつけておく. ### ROM_SIZE: romのサイズ ### ここmmapでいいのかallocate系にすべきなのかとかちょっとよくわかってない. // 領域を確保して uint8_t *mem = mmap(NULL, ROM_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0); // romを確保したところにコピー memcpy(mem, rom_bin, sizeof(rom_bin)); // このromの情報の構造体をioctlでVMに設定する. struct kvm_userspace_memory_region region = { .guest_phys_addr = 0, .memory_size = ROM_SIZE, .userspace_addr = (uin64_t)mem }; ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion); ### VCPUを作成する ### ioctlのあとkvm側で作成されるkvm_runを利用する必要があるのでアクセスできるようにしておく. // 第3引数は作成するvcpu_id int vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0); size_t mmap_size = ioctl(kvmfd, KVM_GET_VCPU_MMAP_SIZE, NULL); struct kvm_run *run = mmap(NULL, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED, vcpufd, 0); ### VCPUのレジスタの初期設定 ☆☆☆よくわかってないからあとで調べ直す. ### SREGSとREGSの二つあるらしく,それぞれ別のioctlで設定や取得をする. // SREGについて ※セグメントレジスタ struct kvm_sregs sregs; ioctl(vcpufd, KVM_GET_SREGS, &sregs); sregs.cs.base = 0; sregs.cs.selector = 0; ioctl(vcpufd, KVM_SET_SREGS, &sregs); ここで、この項のVMは「起動されると0x00000000の命令から実行を始める」こととします。そのための「セグメンテーション」と呼ばれるx86 CPUの機能の設定を行っているのがリスト1.8です。セグメンテーションとはアドレス空間を「セグメント」と呼ぶ領域に分けてアクセスする方式です。セグメントには用途が決まっているものもあり、リスト1.8では「コードセグメント(CS)」という「CPUが実行する命令が配置されているセグメント」の設定を行っています。やっていることは単にCSがアドレス0x00000000から始まる事を設定しているだけです。 // REGSについて ※ステータスレジスタ struct kvm_regs regs = { .rip = 0x0, .rflags = 0x02, /* RFLAGS初期状態 */ }; ioctl(vcpufd, KVM_SET_REGS, ®s); レジスタripはCSの先頭からのオフセットです。KVM_SET_SREGSでCSは0x00000000から始まるように設定したので、ripも0を設定しておくことで、VCPUはVM起動後、0x00000000の命令から実行を始めるようになります。レジスタrflagsはCPUの状態を示すフラグです。予約ビットで1を書くことが決められているビットを除き、すべてのビットを0で初期化します。 ### 実行する ### 特権命令やIO処理があったら動作が戻ってくるので,そこらへんのデバイスエミュレーションとかが本来はある. uint8_t is_running = 1; while (is_running) { ioctl(vcpufd, KVM_RUN, NULL); switch (run->exit_reason) { /* exit_reasonで分岐 エミュレーションとかする */ case KVM_EXIT_HLT: /* HLTした */ /* printf("KVM_EXIT_HLT\n"); */ is_running = 0; fflush(stdout); break; case KVM_EXIT_IO: /* IO操作 */ if (run->io.port == 0x01 && run->io.direction == KVM_EXIT_IO_OUT) { putchar(*(char *)((unsigned char *)run + run->io.data_offset)); } } } とりあえずこれでromがちゃんとしていれば雑に動くVMが立てられるはず. あとは割り込みコントローラとかタイマとか追加して,romがちゃんとBIOSとかになってて, あとRAMもしっかり追加してやらないといけない.