| /* Copyright (c) 2017 Google Inc. |
| * Barret Rhoden <brho@cs.berkeley.edu> |
| * See LICENSE for details. |
| * |
| * vthread_test: mostly create/join/vmcalls */ |
| |
| #include <vmm/vmm.h> |
| #include <vmm/vthread.h> |
| #include <parlib/stdio.h> |
| #include <parlib/uthread.h> |
| |
| enum { |
| MY_VMCALL_TEST1 = VTH_VMCALL_NEXT, |
| MY_VMCALL_TEST2, |
| }; |
| |
| /* Here's how you can make your own vmcalls and still use the vth vmcalls for |
| * the stuff you don't want to reimplement. */ |
| static bool extended_handle_vmcall(struct guest_thread *gth, |
| struct vm_trapframe *vm_tf) |
| { |
| switch (vm_tf->tf_rax) { |
| case MY_VMCALL_TEST1: |
| goto out_ok; |
| } |
| return vth_handle_vmcall(gth, vm_tf); |
| out_ok: |
| vm_tf->tf_rip += 3; |
| return TRUE; |
| }; |
| |
| static struct virtual_machine vm = {.vmcall = extended_handle_vmcall, |
| .mtx = UTH_MUTEX_INIT}; |
| |
| static void thread_entry(void *arg) |
| { |
| const char nums[] = "123456789"; |
| |
| for (int i = 0; i < sizeof(nums); i++) |
| vmcall(VTH_VMCALL_PRINTC, nums[i]); |
| vmcall(MY_VMCALL_TEST1); |
| vmcall(VTH_VMCALL_EXIT, arg, 0, 0, 0); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| #define NR_VTHS 5 |
| struct vthread *vths[NR_VTHS]; |
| void *retvals[NR_VTHS]; |
| |
| /* Tests multiple threads at once */ |
| for (long i = 0; i < NR_VTHS; i++) |
| vths[i] = vthread_create(&vm, thread_entry, (void*)i); |
| for (long i = 0; i < NR_VTHS; i++) { |
| vthread_join(vths[i], &retvals[i]); |
| assert(retvals[i] == (void*)i); |
| } |
| |
| /* Tests reuse / GPC leakage */ |
| for (long i = 0; i < NR_VTHS * 2; i++) { |
| vths[0] = vthread_create(&vm, thread_entry, (void*)i); |
| vthread_join(vths[0], &retvals[0]); |
| assert(retvals[0] == (void*)i); |
| } |
| assert(vm.nr_gpcs == NR_VTHS); |
| |
| return 0; |
| } |