166 lines
4.6 KiB
C
166 lines
4.6 KiB
C
//---
|
|
// hypervisor:wsiwtch - hypervisor world switch
|
|
//---
|
|
#include "fxBoot/hypervisor.h"
|
|
#include "fxBoot/terminal.h"
|
|
|
|
#include <gint/std/stdlib.h>
|
|
#include <gint/atomic.h>
|
|
#include <gint/drivers.h>
|
|
|
|
/* internal globals information */
|
|
HYPERVISOR_BSS void *hypervisor_current_vbr = NULL;
|
|
HYPERVISOR_BSS struct hworld *hypervisor_world_queue = NULL;
|
|
extern struct hworld *hypervisor_world_current;
|
|
extern struct hworld *hypervisor_world;
|
|
|
|
//---
|
|
// World API
|
|
//---
|
|
/* hypervisor_wswitch_world_free(): Free'd allocated world */
|
|
void hypervisor_wswitch_world_free(struct hworld **world)
|
|
{
|
|
if (world == NULL || *world == NULL)
|
|
return;
|
|
if ((*world)->memory.stack.kernel != NULL)
|
|
free((*world)->memory.stack.kernel);
|
|
if ((*world)->memory.stack.user != NULL)
|
|
free((*world)->memory.stack.user);
|
|
if ((*world)->context.drivers != NULL)
|
|
drivers_context_destroy((*world)->context.drivers);
|
|
free(*world);
|
|
*world = NULL;
|
|
}
|
|
|
|
/* hypervisor_wswitch_world_alloc(): Allocate a new world */
|
|
struct hworld *hypervisor_wswitch_world_alloc(void)
|
|
{
|
|
struct hworld *world;
|
|
|
|
world = calloc(sizeof(struct hworld), 1);
|
|
if (world == NULL)
|
|
return (NULL);
|
|
world->private.status = HYPERVISOR_STATUS_CREATED;
|
|
world->private.env = HYPERVISOR_ENV_CASIO;
|
|
world->memory.stack.user = malloc(HYPERVISOR_STACK_USER_SIZE);
|
|
world->memory.stack.kernel = malloc(HYPERVISOR_STACK_KERNEL_SIZE);
|
|
world->context.drivers = drivers_context_create();
|
|
if (world->memory.stack.user == NULL
|
|
|| world->memory.stack.kernel == NULL
|
|
|| world->context.drivers == NULL) {
|
|
hypervisor_wswitch_world_free(&world);
|
|
return (NULL);
|
|
}
|
|
world->memory.stack.size.user = HYPERVISOR_STACK_USER_SIZE;
|
|
world->memory.stack.size.kernel = HYPERVISOR_STACK_USER_SIZE;
|
|
world->stack.kernel = world->memory.stack.kernel;
|
|
world->stack.kernel += HYPERVISOR_STACK_KERNEL_SIZE;
|
|
world->stack.kernel = (void*)((uintptr_t)world->stack.kernel & ~3);
|
|
world->stack.user = world->memory.stack.user;
|
|
world->stack.user += HYPERVISOR_STACK_USER_SIZE;
|
|
world->stack.user = (void*)((uintptr_t)world->stack.user & ~3);
|
|
world->context.cpu.reg[15] = (uintptr_t)world->stack.user;
|
|
return (world);
|
|
}
|
|
|
|
/* hypervisor_wswitch_create(): Create a new world */
|
|
struct hworld *hypervisor_wswitch_create(void)
|
|
{
|
|
struct hworld *world;
|
|
|
|
world = hypervisor_world_queue;
|
|
while (world != NULL) {
|
|
if (world->private.status != HYPERVISOR_STATUS_DEAD) {
|
|
world = world->private.next;
|
|
continue;
|
|
}
|
|
world->private.status = HYPERVISOR_STATUS_INIT;
|
|
return (world);
|
|
}
|
|
return (hypervisor_wswitch_world_alloc());
|
|
}
|
|
|
|
/* void hypervisor_wswitch_destroy(): Destroy the world */
|
|
int hypervisor_wswitch_destroy(struct hworld *world)
|
|
{
|
|
if (world == NULL)
|
|
return (-1);
|
|
if (world->private.status != HYPERVISOR_STATUS_CREATED
|
|
&&world->private.status != HYPERVISOR_STATUS_INIT) {
|
|
return (-2);
|
|
}
|
|
if (world->memory.program.start != NULL) // <-- move me
|
|
free(world->memory.program.start); // <-- loader unload
|
|
if (world->context.drivers != NULL)
|
|
drivers_context_destroy(world->context.drivers);
|
|
world->private.inode = NULL;
|
|
if (world->private.status == HYPERVISOR_STATUS_INIT) {
|
|
world->private.status = HYPERVISOR_STATUS_DEAD;
|
|
return (0);
|
|
}
|
|
free(world);
|
|
return (0);
|
|
}
|
|
|
|
//---
|
|
// Queue API
|
|
//---
|
|
/* hypervisor_wswitch_queue_register(): Register the new world */
|
|
int hypervisor_wswitch_queue_register(struct hworld *world)
|
|
{
|
|
if (world == NULL)
|
|
return (-1);
|
|
world->private.next = hypervisor_world_queue;
|
|
hypervisor_world_queue = world;
|
|
world->private.status = HYPERVISOR_STATUS_INIT;
|
|
return (0);
|
|
}
|
|
|
|
/* hypervisor_wswich_queue_get(): Get the world queue */
|
|
struct hworld *hypervisor_wswitch_queue_get(void)
|
|
{
|
|
return (hypervisor_world_queue);
|
|
}
|
|
|
|
/* hypervisor_wswitch_queue_unregister(): Unregister a world */
|
|
int hypervisor_wswitch_queue_unregister(struct hworld *target)
|
|
{
|
|
struct hworld **world;
|
|
|
|
world = &hypervisor_world_queue;
|
|
while (*world != NULL) {
|
|
if (*world != target) {
|
|
world = &(*world)->private.next;
|
|
continue;
|
|
}
|
|
*world = (*world)->private.next;
|
|
if (target->private.status != HYPERVISOR_STATUS_DEAD)
|
|
hypervisor_wswitch_destroy(target);
|
|
return (0);
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
//---
|
|
// User API
|
|
//---
|
|
/* hypervisor_wswitch(): Perform a world switch */
|
|
int hypervisor_wswitch(struct hworld *world)
|
|
{
|
|
if (world == NULL)
|
|
return (-1);
|
|
if (world->private.status != HYPERVISOR_STATUS_INIT)
|
|
return (-2);
|
|
int retval;
|
|
hypervisor_install();
|
|
tracer_set_session(world->private.debug.tracer);
|
|
terminal_error("world switch to:\ncurr: %p\nnext: %p\n",
|
|
hypervisor_world_current, world);
|
|
__asm__ volatile (
|
|
"mov %0, r4;"
|
|
"trapa #0;"
|
|
"mov r0, %0;"
|
|
: "=r"(retval) : "r"(world) : "r4", "r0");
|
|
return (retval);
|
|
}
|