Hatena::ブログ(Diary)

go_vmの日記

2011-01-20

Kernel/VM Advent Calendar 46日目: VM内Brainf**k

はじめに

この記事は Kernel/VM Advent Calendar のための記事です。

この記事をご覧になる皆様は、日常的にカーネル内で動作するコードを書いていると思います。たとえばLinuxでHello Worldを書きたいなと言うときには、カーネル内にprintkが用意されているのでとても便利ですね。すぐに文字列を出力できます。

一方で、カーネル内でコードを書くときに困るのが文字列の入力です。カーネル内だけではどうにも難しいので、ユーザ空間から入力をもらってくる必要があります。その方法は様々。

ここでは、Kernel/VM Advent Calendar 3日目にならってカーネル内にBrainfuckインタプリタを実装し、さらにユーザ空間からキーボードでプログラムを入力できるようにしました。環境は次のとおり。

ホストOSLinux 2.6.32 (x86)
VMMLinux KVM 2.6.32/qemu-kvm 0.13.0
ゲストOSOpenBSD 4.8

今回はKVMを用いて、ゲストOSのカーネルにBrainfuckを実装します。へんなコードを書いても

実機に影響が無いので安心快適です。

先に結果

OpenBSDのカーネルに次のようにBrainfuckを実装し、KVMのゲストOSとして動作させています。

static void exec_bf(const char *tape,char *mem,char *output)
{
  int eptr = 0;
  int mptr = 0;
  int optr = 0;
  int ecnt = 0;

  while(tape[eptr] != '\0'){
    switch(tape[eptr]){
    case '>':
      mptr++;
      break;
    case '<':
      mptr--;
      break;
    case '+':
      mem[mptr]++;
      break;
    case '-':
      mem[mptr]--;
      break;
    case '.':
      output[optr++] = mem[mptr];
      break;
    case ',':
      break;
    case '[':
      {
        int c = 0;
        if(mem[mptr] != 0) break;
        while(1){
          if(tape[eptr] == '['){
            c++;
          }else if(tape[eptr] == ']'){
            c--;
            if(c == 0) break;
          }
          eptr++;
          ecnt++;
          if(ecnt > 10000) goto exec_limit;
         }
         break;
       }
     case ']':
       {
         int c = 0;
         while(1){
           if(tape[eptr] == ']'){
             c++;
           }else if(tape[eptr] == '['){
             c--;
             if(c == 0) break;
           }
           eptr--;
           ecnt++;
           if(ecnt > 10000) goto exec_limit;
         }
         eptr--;
        }
        break;
      default:
       // do nothing
       break;
     }
     eptr++;
     ecnt++;
     if(ecnt > 10000) goto exec_limit;
   }

   output[optr] = '\0';
   return;

exec_limit:
   printf("vmbf: exec_limit: e:%d m:%d o:%d\n",eptr,mptr,optr);
   output[0] = '\0';
}

引数のtapeが入力するプログラムです。memが作業領域で、結果はoutputに入ってくる。

このゲストカーネル内コードに対して、ホストOS側のユーザ空間から入力を与えます。

Brainfuckプログラムを入力します。ホストOS側でQEMU Monitorを開き、次のように入力すると実行できます。当然ですがQEMU MonitorはホストOS側のプログラムです。

QEMU 0.13.0 monitor - type 'help' for more information
(qemu) vmbf_run +++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+.
(qemu) vmbf_show 
vmbf: Hello, world!
(qemu) 

(ゲストOSの)カーネル内のBrainfuckインタプリタの実装に対し、(ホストOSの)ユーザ空間からキーボード入力を渡し、(ホストOSの)ユーザ空間に結果を出力しました。

何が起きているか

次の事が起きています

  1. [ホスト] QEMU Monitor にBrainfuckのプログラムを入力する
  2. [ホスト] 入力されたプログラムをゲストOSのメモリ領域に書き込む (vmbf_run)
  3. [ホスト] ゲストOSに割り込みをかける
  4. [ゲスト] 割り込みハンドラが入力を拾う
  5. [ゲスト] ゲストのカーネルスレッドが入力されたBrainfuckを実行
  6. [ゲスト] 実行結果をゲストのメモリに書き込む
  7. [ホスト] 実行結果をゲストのメモリから読み込む
  8. [ホスト] QEMU Monitorに結果が表示される (vmbf_show)

実装について

上のような動作を実現するために、Linux KVM/qemu-kvm、そしてOpenBSDに対して変更を

行いました。それぞれのパッチを記事の最後に貼ります。

このパッチには、次のような要素が含まれています。

ハイパーコール

仮想計算機のゲストOSがVMを呼び出すことをハイパーコールとか、ハイパーバイザコールなどと呼びます。今回はゲストOSからホストOSへ、Brainfuckプログラムを書き込むためのメモリ領域を通知するために用いています。このために、ゲストのOpenBSDにIntel-VTのvmcall命令を用いてハイパーコールを行う機能を、ホストのLinux KVM/qemu-kvmに、ハイパーコールを受け取る機能を追加しました。

アドレス変換

ゲストOSとホストOSでは独立したアドレス空間を持っているので、ハイパーコールでメモリアドレスを渡しただけでは、お互いに読み書きすることができません。今回は、ホストのユーザ空間で動作しているqemu-kvmの中で、ゲストの物理アドレスをホストの仮想アドレスに変換する機能を使えるようにしました。

QEMU Monitor

QEMU Monitorはqemuの提供する機能の一つで、ここにコマンドを打ち込むことで動作中のVMに様々な操作をすることができます。なかなかおもしろいので、今回はこのQEMU Monitor経由でゲストOSにアクセスする機能を追加しました。パッチには、QEMU Monitorに新しいコマンドを追加する方法が含まれています。


まとめ

カーネル内のコードにユーザ空間から簡単にアクセスする方法を紹介しました。

カーネルをいじるのは怖くありません。あとVMをいじるのも怖くありません。

おわりに

本当はどんなコードがどこで動いているのか順を追って説明するつもりだったのですが、ホストのユーザ空間とホストのカーネルとゲストのカーネルに渡ったプログラムを文章で説明するのは難しいです。詳しくはコード本体を見てもらうとわかると思います。

久しぶりに頭の悪いコードを書いたので、ちょっと脳から汁が出て楽しかったです。


ホスト側qemu-kvm

From 70b5ed05320da1605a1185458bae5501d5252e9c Mon Sep 17 00:00:00 2001
From: Go Saito <go at softlab.cs.tsukuba.ac.jp>
Date: Fri, 21 Jan 2011 01:17:44 +0900
Subject: [PATCH] Kernel/VM AC: Brainfuck VM

patch for host qemu-kvm

Signed-off-by: Go Saito <go at softlab.cs.tsukuba.ac.jp>
---
 Makefile.target |    2 +
 hw/pc_piix.c    |    4 ++
 hypercall.c     |   91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hypercall.h     |   24 ++++++++++++++
 kvm-all.c       |   11 +++++++
 monitor.c       |   22 +++++++++++++
 qemu-kvm.c      |   27 ++++++++++++++++
 qemu-monitor.hx |   16 ++++++++++
 8 files changed, 197 insertions(+), 0 deletions(-)
 create mode 100644 hypercall.c
 create mode 100644 hypercall.h

diff --git a/Makefile.target b/Makefile.target
index 9643f88..485a241 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -184,6 +184,8 @@ obj-y += rwhandler.o
 obj-$(CONFIG_KVM) += kvm.o kvm-all.o
 obj-$(CONFIG_NO_KVM) += kvm-stub.o
 
+obj-y += hypercall.o
+
 # MSI-X depends on kvm for interrupt injection,
 # so moved it from Makefile.objs to Makefile.target for now
 obj-y += msix.o
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 9e4bac8..270bdc0 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -35,6 +35,8 @@
 #include "sysemu.h"
 #include "sysbus.h"
 
+#include "hypercall.h"
+
 qemu_irq *ioapic_irq_hack;
 
 #define MAX_IDE_BUS 2
@@ -117,6 +119,8 @@ static void pc_init1(ram_addr_t ram_size,
         isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24);
     }
 
+       hc_set_qemu_irq(i8259);
+
     if (pci_enabled) {
         pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size);
     } else {
diff --git a/hypercall.c b/hypercall.c
new file mode 100644
index 0000000..2bcb36c
--- /dev/null
+++ b/hypercall.c
@@ -0,0 +1,91 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include "qemu-common.h"
+#include "qemu-kvm.h"
+#include "hw/irq.h"
+#include "hypercall.h"
+
+static qemu_irq *i8259;
+
+static char *guest_tape;
+static char *guest_output;
+static char *guest_state;
+
+#define VMBF_READY 0
+#define VMBF_RUN 1
+#define VMBF_DONE 2
+#define VMBF_INIT 3
+
+#define VMBF_IRQ 5
+
+void *gpa_to_hva(uint64_t addr)
+{
+       extern int hc_kvm_vm_ioctl(int,void*);
+       struct kvm_hc_gpa_to_hva gh;
+       int r;
+
+       gh.gpa = addr;
+       r = hc_kvm_vm_ioctl(KVM_HC_GPA_TO_HVA,&gh);
+       if(r != 0){
+               perror("KVM_HC_GPA_TO_HVA");
+               return 0;
+       }
+       return (void*)gh.hva;
+}
+
+void hc_set_qemu_irq(qemu_irq *irq)
+{
+       i8259 = irq;
+}
+
+void hc_set_irq(int irq)
+{
+       qemu_irq_pulse(i8259[irq]);
+}
+
+void vmbf_set_tape(const char *tape)
+{
+       strcpy(guest_tape,tape);
+}
+
+void vmbf_run_bf(void)
+{
+       *guest_state = VMBF_READY;
+       hc_set_irq(VMBF_IRQ);
+}
+
+char *vmbf_get_output(void)
+{
+       if(*guest_state != VMBF_DONE)
+               return NULL;
+       return guest_output;
+}
+
+int do_hypercall(kvm_context_t kvm,CPUState *env,struct kvm_regs *regs)
+{
+       unsigned long *arg;
+       unsigned long op,magic,size;
+
+       printf("HOST: HC!\n");
+
+       arg = gpa_to_hva(regs->rcx);
+       op = regs->rbx;
+       magic = arg[0];
+       size = regs->rdx;
+
+       printf("HOST: hypercall: %ld %lx %ld",op,magic,size);
+
+       guest_tape   = gpa_to_hva(arg[1]);
+       guest_output = gpa_to_hva(arg[2]);
+       guest_state  = gpa_to_hva(arg[3]);
+
+       return 999;
+}
+
diff --git a/hypercall.h b/hypercall.h
new file mode 100644
index 0000000..923a6ea
--- /dev/null
+++ b/hypercall.h
@@ -0,0 +1,24 @@
+#ifndef HYPERCALL_H
+#define HYPERCALL_H
+
+#include <linux/kvm.h>
+#include "qemu-common.h"
+#include "qemu-kvm.h"
+
+struct kvm_hc_gpa_to_hva {
+       uint64_t gpa;
+       unsigned long hva;
+};
+#define KVM_HC_GPA_TO_HVA _IOWR(KVMIO,0xe1,struct kvm_hc_gpa_to_hva)
+
+void *gpa_to_hva(uint64_t addr);
+void hc_set_qemu_irq(qemu_irq *irq);
+void hc_set_irq(int irq);
+void vmbf_set_tape(const char *ape);
+void vmbf_run_bf(void);
+char *vmbf_get_output(void);
+int do_hypercall(kvm_context_t kvm,CPUState *env,struct kvm_regs *reg);
+
+
+#endif // HYPERCALL_H
+
diff --git a/kvm-all.c b/kvm-all.c
index 00cecf6..a131314 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -1000,6 +1000,17 @@ int kvm_vm_ioctl(KVMState *s, int type, ...)
     return ret;
 }
 
+int hc_kvm_vm_ioctl(int,void*);
+int hc_kvm_vm_ioctl(int type,void *arg)
+{
+       int ret;
+
+       ret = ioctl(kvm_state->vmfd,type,arg);
+       if(ret == -1)
+               ret = -errno;
+       return ret;
+}
+
 int kvm_vcpu_ioctl(CPUState *env, int type, ...)
 {
     int ret;
diff --git a/monitor.c b/monitor.c
index dd5469f..0a24df2 100644
--- a/monitor.c
+++ b/monitor.c
@@ -58,6 +58,8 @@
 #include "exec-all.h"
 #include "qemu-kvm.h"
 
+#include "hypercall.h"
+
 //#define DEBUG
 //#define DEBUG_COMPLETION
 
@@ -1062,6 +1064,26 @@ static void do_log(Monitor *mon, const QDict *qdict)
     cpu_set_log(mask);
 }
 
+static void do_vmbf_run(Monitor *mon, const QDict *qdict)
+{
+    const char *bf = qdict_get_str(qdict, "bf");
+
+       vmbf_set_tape(bf);
+       vmbf_run_bf();
+}
+
+static void do_vmbf_show(Monitor *mon, const QDict *qdict)
+{
+       char *str;
+
+       str = vmbf_get_output();
+       if(str){
+               monitor_printf(mon,"vmbf: %s\n",str);
+       }else{
+               monitor_printf(mon,"vmbf madadayo\n");
+       }
+}
+
 static void do_singlestep(Monitor *mon, const QDict *qdict)
 {
     const char *option = qdict_get_try_str(qdict, "option");
diff --git a/qemu-kvm.c b/qemu-kvm.c
index 4f7cf6d..9c1bcfa 100644
--- a/qemu-kvm.c
+++ b/qemu-kvm.c
@@ -30,6 +30,8 @@
 #include "compatfd.h"
 #include <sys/prctl.h>
 
+#include "hypercall.h"
+
 #define false 0
 #define true 1
 
@@ -555,6 +557,22 @@ int handle_shutdown(kvm_context_t kvm, CPUState *env)
     return 1;
 }
 
+static int handle_hypercall(kvm_context_t kvm,CPUState *env)
+{
+       int r;
+       struct kvm_regs regs;
+
+       r = kvm_get_regs(env,&regs);
+       if(r < 0){
+               perror("KVM_GET_REGS");
+               env->kvm_run->hypercall.ret = -1;
+               return 0;
+       }
+
+       env->kvm_run->hypercall.ret = do_hypercall(kvm,env,&regs);
+       return 0;
+}
+
 static inline void push_nmi(kvm_context_t kvm)
 {
 #ifdef KVM_CAP_USER_NMI
@@ -671,10 +689,19 @@ int kvm_run(CPUState *env)
             r = kvm_s390_handle_reset(kvm, env, run);
             break;
 #endif
+               case KVM_EXIT_HYPERCALL:
+                       r = handle_hypercall(kvm,env);
+                       break;
+#ifdef KVM_CAP_INTERNAL_ERROR_DATA
+/*
+ * https://bugs.launchpad.net/qemu/+bug/680350
+ * ???
+ */
        case KVM_EXIT_INTERNAL_ERROR:
             kvm_handle_internal_error(env, run);
             r = 1;
            break;
+#endif
         default:
             if (kvm_arch_run(env)) {
                 fprintf(stderr, "unhandled vm exit: 0x%x\n", run->exit_reason);
diff --git a/qemu-monitor.hx b/qemu-monitor.hx
index 595d362..a315090 100644
--- a/qemu-monitor.hx
+++ b/qemu-monitor.hx
@@ -126,6 +126,22 @@ Example:
 
 EQMP
 
+       {
+               .name           = "vmbf_run",
+               .args_type  = "bf:s",
+               .params         = "bf",
+               .help           = "run bf",
+               .mhandler.cmd = do_vmbf_run,
+       },
+
+       {
+               .name           = "vmbf_show",
+               .args_type      = "",
+               .params         = "",
+               .help           = "show bf output",
+               .mhandler.cmd = do_vmbf_show,
+       },
+
     {
         .name       = "eject",
         .args_type  = "force:-f,device:B",
-- 
1.7.0.4

ホスト側kvm

From 32e281f81e8d9f5d9d1b2fde875395afc6ec15d7 Mon Sep 17 00:00:00 2001
From: Go Saito <go at softlab.cs.tsukuba.ac.jp>
Date: Fri, 21 Jan 2011 01:24:37 +0900
Subject: [PATCH] Kernel/VM AC: Brainfuck VM

patch for host kvm

Signed-off-by: Go Saito <go at softlab.cs.tsukuba.ac.jp>
---
 arch/x86/kvm/svm.c  |    2 +-
 arch/x86/kvm/vmx.c  |    2 +-
 arch/x86/kvm/x86.c  |    6 ++++++
 virt/kvm/kvm_main.c |   36 ++++++++++++++++++++++++++++++++++++
 4 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 52ffdc6..0999bda 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -1424,7 +1424,7 @@ static int vmmcall_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
 {
        svm->next_rip = kvm_rip_read(&svm->vcpu) + 3;
        skip_emulated_instruction(&svm->vcpu);
-       kvm_emulate_hypercall(&svm->vcpu);
+       return kvm_emulate_hypercall(&svm->vcpu);
        return 1;
 }
 
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 6a28d5d..5f65950 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -3129,7 +3129,7 @@ static int handle_halt(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 static int handle_vmcall(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 {
        skip_emulated_instruction(vcpu);
-       kvm_emulate_hypercall(vcpu);
+       return kvm_emulate_hypercall(vcpu);
        return 1;
 }
 
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 7bcef64..d7000ac 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3301,6 +3301,7 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
 {
        unsigned long nr, a0, a1, a2, a3, ret;
        int r = 1;
+#define KVM_HC_RET_TO_USER 12345
 
        nr = kvm_register_read(vcpu, VCPU_REGS_RAX);
        a0 = kvm_register_read(vcpu, VCPU_REGS_RBX);
@@ -3330,6 +3331,11 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
        case KVM_HC_MMU_OP:
                r = kvm_pv_mmu_op(vcpu, a0, hc_gpa(vcpu, a1, a2), &ret);
                break;
+       case KVM_HC_RET_TO_USER:
+               ret = 0;
+               vcpu->run->exit_reason = KVM_EXIT_HYPERCALL;
+               r = 0;
+               break;
        default:
                ret = -KVM_ENOSYS;
                break;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 4f3434f..123727d 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -2106,6 +2106,28 @@ out:
        return r;
 }
 
+struct kvm_hc_gpa_to_hva {
+       uint64_t gpa;
+       unsigned long hva;
+};
+#define KVM_HC_GPA_TO_HVA _IOWR(KVMIO,0xe1,struct kvm_hc_gpa_to_hva)
+
+static int kvm_vm_ioctl_hc_gpa_to_hva(struct kvm *kvm,struct kvm_hc_gpa_to_hva *gh)
+{
+       unsigned long hva;
+       gfn_t gfn;
+
+       gfn = gh->gpa >> PAGE_SHIFT;
+       hva = gfn_to_hva(kvm,gfn);
+       if(kvm_is_error_hva(hva)){
+               printk(KERN_WARNING "hc: invalid address 0x%llx\n",gh->gpa);
+               return -EFAULT;
+       }
+       hva += offset_in_page(gh->gpa);
+       gh->hva = hva;
+       return 0;
+}
+
 static long kvm_vm_ioctl(struct file *filp,
                           unsigned int ioctl, unsigned long arg)
 {
@@ -2306,6 +2328,20 @@ static long kvm_vm_ioctl(struct file *filp,
                mutex_unlock(&kvm->lock);
                break;
 #endif
+       case KVM_HC_GPA_TO_HVA: {
+               struct kvm_hc_gpa_to_hva gh;
+               r = -EFAULT;
+               if(copy_from_user(&gh,argp,sizeof gh))
+                       goto out;
+               r = kvm_vm_ioctl_hc_gpa_to_hva(kvm,&gh);
+               if(r)
+                       goto out;
+               if(copy_to_user(argp,&gh,sizeof gh)){
+                       r = -EFAULT;
+                       goto out;
+               }
+               break;
+       }
        default:
                r = kvm_arch_vm_ioctl(filp, ioctl, arg);
        }
-- 
1.7.0.4

ゲスト側OpenBSD

From 3676983a5660e7ca91eb71ab1d7f53229ca25225 Mon Sep 17 00:00:00 2001
From: Go Saito <go at softlab.cs.tsukuba.ac.jp>
Date: Fri, 21 Jan 2011 01:35:03 +0900
Subject: [PATCH] Kernel/VM AC: Brainfuck VM

patch for guest openbsd48

Signed-off-by: Go Saito <go at softlab.cs.tsukuba.ac.jp>
---
 conf/files       |    2 +
 kern/init_main.c |    3 +
 kern/vmbf.c      |  187 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 192 insertions(+), 0 deletions(-)
 create mode 100644 kern/vmbf.c

diff --git a/conf/files b/conf/files
index 2a9f3db..5457e8e 100644
--- a/conf/files
+++ b/conf/files
@@ -1115,3 +1115,5 @@ file lib/libkern/arch/${MACHINE_ARCH}/skpc.S | lib/libkern/skpc.c
 file lib/libkern/arch/${MACHINE_ARCH}/htonl.S | lib/libkern/htonl.c
 file lib/libkern/arch/${MACHINE_ARCH}/htons.S | lib/libkern/htons.c
 file lib/libkern/arch/${MACHINE_ARCH}/strncasecmp.S | lib/libkern/strncasecmp.c
+
+file kern/vmbf.c
diff --git a/kern/init_main.c b/kern/init_main.c
index 14afa2a..0b6ea3f 100644
--- a/kern/init_main.c
+++ b/kern/init_main.c
@@ -194,6 +194,7 @@ main(void *framep)
        extern void disk_init(void);
        extern void endtsleep(void *);
        extern void realitexpire(void *);
+       extern void vmbf_init(void);
 
        /*
         * Initialize the current process pointer (curproc) before
@@ -541,6 +542,8 @@ main(void *framep)
 
        domountroothooks();
 
+       vmbf_init();
+
        /*
         * Okay, now we can let init(8) exec!  It's off to userland!
         */
diff --git a/kern/vmbf.c b/kern/vmbf.c
new file mode 100644
index 0000000..ea0a09a
--- /dev/null
+++ b/kern/vmbf.c
@@ -0,0 +1,187 @@
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/workq.h>
+#include <sys/malloc.h>
+#include <uvm/uvm.h>
+
+#include <machine/i8259.h>
+#include <machine/i82093var.h>
+#include <machine/intr.h>
+
+#define KVM_HC_RET_TO_USER 12345
+#define NAZO_IRQ_5 0x10010505
+
+static void *ih_vmbf;
+
+static char vmbf_workmem[1024];
+
+static char vmbf_tape[1024];
+static char vmbf_output[1024];
+static char vmbf_state;
+#define VMBF_READY 0
+#define VMBF_RUN 1
+#define VMBF_DONE 2
+#define VMBF_INIT 3
+
+#define VTOPHYS(v) vtophys((vaddr_t)(v))
+
+int vmbf_init(void);
+
+static int
+vmmcall(unsigned long b,unsigned long c,unsigned long d)
+{
+       int ret;
+       __asm__ __volatile__
+               (".byte 0xf,0x1,0xc1"
+                : "=a" (ret)
+                : "a" (KVM_HC_RET_TO_USER),
+                  "b" (b),
+                  "c" (c),
+                  "d" (d)
+               );
+       return ret;
+}
+
+static void exec_bf(const char *tape,char *mem,char *output)
+{
+       int eptr = 0;
+       int mptr = 0;
+       int optr = 0;
+       int ecnt = 0;
+
+       while(tape[eptr] != '\0'){
+               switch(tape[eptr]){
+               case '>':
+                       mptr++;
+                       break;
+               case '<':
+                       mptr--;
+                       break;
+               case '+':
+                       mem[mptr]++;
+                       break;
+               case '-':
+                       mem[mptr]--;
+                       break;
+               case '.':
+                       output[optr++] = mem[mptr];
+                       break;
+               case ',':
+                       break;
+               case '[':
+                       {
+                               int c = 0;
+                               if(mem[mptr] != 0) break;
+                               while(1){
+                                       if(tape[eptr] == '['){
+                                               c++;
+                                       }else if(tape[eptr] == ']'){
+                                               c--;
+                                               if(c == 0) break;
+                                       }
+                                       eptr++;
+                                       ecnt++;
+                                       if(ecnt > 10000) goto exec_limit;
+                               }
+                               break;
+                       }
+               case ']':
+                       {
+                               int c = 0;
+                               while(1){
+                                       if(tape[eptr] == ']'){
+                                               c++;
+                                       }else if(tape[eptr] == '['){
+                                               c--;
+                                               if(c == 0) break;
+                                       }
+                                       eptr--;
+                                       ecnt++;
+                                       if(ecnt > 10000) goto exec_limit;
+                               }
+                               eptr--;
+                       }
+                       break;
+               default:
+                       // do nothing
+                       break;
+               }
+               eptr++;
+               ecnt++;
+               if(ecnt > 10000) goto exec_limit;
+       }
+
+       output[optr] = '\0';
+       return;
+
+exec_limit:
+       printf("vmbf: exec_limit: e:%d m:%d o:%d\n",eptr,mptr,optr);
+       output[0] = '\0';
+}
+
+static void vmbf_task(void *_tape,void *_state)
+{
+       char *tape = (char*) _tape;
+       char *state = (char*) _state;
+
+       memset(vmbf_workmem,0,sizeof(vmbf_workmem));
+       memset(vmbf_output,0,sizeof(vmbf_output));
+
+       exec_bf(tape,vmbf_workmem,vmbf_output);
+       *state = VMBF_DONE;
+}
+
+static int
+vmbf_interrupt(void *arg)
+{
+       int claimed = 0;
+
+       if(vmbf_state == VMBF_READY){
+               printf("vmbf: %s\n",vmbf_tape);
+               vmbf_state = VMBF_RUN;
+               workq_add_task(NULL,0,vmbf_task,vmbf_tape,&vmbf_state);
+               claimed = 1;
+       }
+
+       return claimed;
+}
+
+
+int
+vmbf_init(void)
+{
+       unsigned long arg[4];
+       int ret;
+       void *ih;
+
+       memset(vmbf_tape,0,sizeof(vmbf_tape));
+       memset(vmbf_output,0,sizeof(vmbf_output));
+       vmbf_state = VMBF_INIT;
+
+       arg[0] = 0xdeadbeef;
+       arg[1] = VTOPHYS(vmbf_tape);
+       arg[2] = VTOPHYS(vmbf_output);
+       arg[3] = VTOPHYS(&vmbf_state);
+
+       ret = vmmcall(0,VTOPHYS(arg),sizeof(arg));
+
+       if(ret != 999){
+               printf("GUEST: ERROR: vmbf init\n");
+               return -1;
+       }else{
+               printf("GUEST: vmbf init\n");
+       }
+
+       ih = apic_intr_establish(NAZO_IRQ_5,IST_EDGE,IPL_SOFTNET,vmbf_interrupt,
+                       NULL,"vmbf");
+       if(!ih){
+               printf("GUEST: ERROR: vmbf_init: apic_intr_establish\n");
+               return  -1;
+       }
+
+       ih_vmbf = ih;
+       
+       return 0;
+}
-- 
1.7.1

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証