diff options
Diffstat (limited to 'src/vmach.c')
-rw-r--r-- | src/vmach.c | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/src/vmach.c b/src/vmach.c new file mode 100644 index 0000000..a9ca446 --- /dev/null +++ b/src/vmach.c @@ -0,0 +1,120 @@ +#include <libtf/defines.h> +#include <libtf/list.h> +#include <libtf/vmach.h> +#include <libtf/io.h> +#include "uctx.h" + +__thread struct tf_fiber *tf_current_fiber; +__thread struct tf_vcpu *tf_current_vcpu; +__thread struct tf_vmach *tf_current_vmach; + +extern struct tf_poll_hooks tf_epoll_hooks; + +struct tf_vmachine { + struct tf_vmach vmach; + struct tf_uctx startup_uctx; + void *machine_init_fiber; +}; + +static void tf_vcpu_main(void *fiber_data) +{ + struct tf_vcpu *vcpu = fiber_data; + struct tf_vmach *vmach = vcpu->machine; + struct tf_fiber *self = container_of(fiber_data, struct tf_fiber, data); + struct tf_fiber *f; + + tf_current_vmach = vmach; + tf_current_vcpu = vcpu; + + while (vmach->num_user_fibers != 0) { + if (tf_list_empty(&vmach->run_q)) { + /* sleep */ + continue; + } + + f = tf_list_entry(tf_list_pop(&vmach->run_q), + struct tf_fiber, queue_node); + + f->return_context = self->context; + tf_current_fiber = f; + tf_uctx_transfer(self->context, f->context); + tf_list_splice_tail(&f->wakeup_q, &vmach->run_q); + } +} + +static void tf_vmach_main(void *fiber_data) +{ + struct tf_fiber *self = container_of(fiber_data, struct tf_fiber, data); + struct tf_vcpu *vcpu = fiber_data; + struct tf_vmach *vmach = vcpu->machine; + + tf_current_vmach = vmach; + tf_current_vcpu = vcpu; + tf_current_fiber = self; + + /* Initialize IO subsystem */ + vmach->poll_ops = &tf_epoll_hooks; + vmach->poll_fiber = vmach->poll_ops->create(); + vmach->timeout_fiber = tf_timeout_fiber_create(); + vmach->startup_fiber.timeout.manager = vmach->timeout_fiber; + + /* Run the initial fiber */ + tf_fiber_wakeup(&vmach->startup_fiber); + + /* Use main thread as a regular vcpu */ + vmach->num_user_fibers = 1; + tf_list_splice_tail(&self->wakeup_q, &vmach->run_q); + tf_vcpu_main(vcpu); + + /* Kill all stuff */ + + /* Return to main fiber */ + vmach->startup_fiber.return_context = NULL; + tf_current_fiber = NULL; + tf_current_vcpu = NULL; + tf_current_vmach = NULL; + + tf_uctx_transfer(self->context, vmach->startup_fiber.context); +} + +void tf_vmach_start(void) +{ + struct tf_vmachine *vmach; + struct tf_vcpu *vcpu; + + TF_BUG_ON(tf_current_vcpu != NULL); + + /* Create a self-fiber so we can surrender control to vcpu */ + vmach = calloc(1, sizeof(struct tf_vmachine)); + vmach->vmach.startup_fiber = (struct tf_fiber) { + .ref_count = 1, + .queue_node = TF_LIST_INITIALIZER(vmach->vmach.startup_fiber.queue_node), + .wakeup_q = TF_LIST_HEAD_INITIALIZER(vmach->vmach.startup_fiber.wakeup_q), + .context = tf_uctx_create_self(&vmach->startup_uctx), + }; + tf_list_init_head(&vmach->vmach.run_q); + vcpu = tf_fiber_create(tf_vmach_main, sizeof(struct tf_vcpu)); + vmach->machine_init_fiber = vcpu; + vcpu->machine = &vmach->vmach; + + /* Create manager fiber to initialize vcpu */ + tf_uctx_transfer(vmach->vmach.startup_fiber.context, + container_of((void *) vcpu, struct tf_fiber, data)->context); +} + +void tf_vmach_stop(void) +{ + struct tf_fiber *self = tf_vmach_get_current_fiber(); + struct tf_vmach *vmach = tf_vmach_get_current(); + + TF_BUG_ON(self != &vmach->startup_fiber); + + /* Wait for the vmachine to stop */ + tf_vmach_get_current()->num_user_fibers--; + while (self->return_context != NULL) + tf_fiber_schedule(); + + /* And clean up */ + tf_uctx_destroy(vmach->startup_fiber.context); + free(vmach); +} |