Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions libsel4vm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ config_string(
"LibSel4VMVMXTimerDebug"
)

config_option(
LibSel4VMUseTranslationVspace
LIB_SEL4VM_USE_TRANSLATION_VSPACE
"Setup a translation vspace to track VM's pages in VMM's vspace.
This prevents VMMs from mapping/unmapping guest pages while reading from or
writing to guest memory. This is required when running a multicore VM setup
due to conflicting reservations by the VMMs"
DEFAULT
OFF
)

mark_as_advanced(LibSel4VMDeferMemoryMap LibSel4VMVMXTimerDebug LibSel4VMVMXTimerTimeout)

add_config_library(sel4vm "${configure_string}")
Expand Down
20 changes: 20 additions & 0 deletions libsel4vm/src/guest_ram.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@
#include <sel4vm/guest_vm.h>
#include <sel4vm/guest_ram.h>
#include <sel4vm/guest_memory.h>
#include <sel4vm/gen_config.h>

#include <sel4utils/vspace_internal.h>

#include "guest_memory.h"
#include "guest_vspace.h"

struct guest_mem_touch_params {
void *data;
Expand Down Expand Up @@ -167,8 +171,24 @@ int vm_ram_touch(vm_t *vm, uintptr_t addr, size_t size, ram_touch_callback_fn to
access_cookie.size = next_addr - current_addr;
access_cookie.offset = current_addr - addr;
access_cookie.current_addr = current_addr;
#ifdef CONFIG_LIB_SEL4VM_USE_TRANSLATION_VSPACE
struct sel4utils_alloc_data *data = get_alloc_data(&vm->mem.vm_vspace);
ZF_LOGF_IF(NULL == data, "Failed to get alloc data");
guest_vspace_t *guest_vspace = (guest_vspace_t *) data;

void *vaddr = (void *)sel4utils_get_cookie(&guest_vspace->translation_vspace, (void *)current_aligned);
if (!vaddr) {
ZF_LOGE("Failed to get cookie at %p", (void *)current_aligned);
return -1;
}

int result = touch_callback(vm, current_addr, (void *)(vaddr + (current_addr - current_aligned)),
next_addr - current_addr, current_addr - addr, cookie);
#else

int result = vspace_access_page_with_callback(&vm->mem.vm_vspace, &vm->mem.vmm_vspace, (void *)current_aligned,
seL4_PageBits, seL4_AllRights, 1, touch_access_callback, &access_cookie);
#endif
if (result) {
return result;
}
Expand Down
86 changes: 62 additions & 24 deletions libsel4vm/src/guest_vspace.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,6 @@
#include "guest_vspace.h"
#include "guest_vspace_arch.h"

typedef struct guest_iospace {
seL4_CPtr iospace;
struct sel4utils_alloc_data iospace_vspace_data;
vspace_t iospace_vspace;
} guest_iospace_t;

typedef struct guest_vspace {
/* We abuse struct ordering and this member MUST be the first
* thing in the struct */
struct sel4utils_alloc_data vspace_data;
/* debug flag for checking if we add io spaces late */
int done_mapping;
int num_iospaces;
guest_iospace_t **iospaces;
} guest_vspace_t;

static int guest_vspace_map(vspace_t *vspace, seL4_CPtr cap, void *vaddr, seL4_CapRights_t rights,
int cacheable, size_t size_bits)
{
Expand All @@ -41,21 +25,51 @@ static int guest_vspace_map(vspace_t *vspace, seL4_CPtr cap, void *vaddr, seL4_C
if (error) {
return error;
}

#if defined(CONFIG_TK1_SMMU) || defined(CONFIG_IOMMU)
struct sel4utils_alloc_data *data = get_alloc_data(vspace);
/* this type cast works because the alloc data was at the start of the struct
* so it has the same address.
* This conversion is guaranteed to work by the C standard */
guest_vspace_t *guest_vspace = (guest_vspace_t *) data;

cspacepath_t orig_path, new_path;
vka_cspace_make_path(guest_vspace->vspace_data.vka, cap, &orig_path);

#ifdef CONFIG_LIB_SEL4VM_USE_TRANSLATION_VSPACE
/* duplicate the cap so we can do a mapping */
error = vka_cspace_alloc_path(guest_vspace->vspace_data.vka, &new_path);
if (error) {
ZF_LOGE("Failed to allocate cslot to duplicate frame cap");
return error;
}

error = vka_cnode_copy(&new_path, &orig_path, seL4_AllRights);
if (error) {
ZF_LOGE("Failed to duplicate frame cap");
return error;
}

/* perform the regular mapping */
void *vmm_vaddr = vspace_map_pages(&guest_vspace->vmm_vspace, &new_path.capPtr, NULL, seL4_AllRights, 1, size_bits,
cacheable);
if (!vmm_vaddr) {
ZF_LOGE("Failed to map into VMM vspace");
return -1;
}

/* add translation information. give dummy cap value of 42 as it cannot be zero
* but we really just want to store information in the cookie */
error = update_entries(&guest_vspace->translation_vspace, (uintptr_t)vaddr, 42, size_bits, (uintptr_t)vmm_vaddr);
if (error) {
ZF_LOGE("Failed to add translation information");
return error;
}
#endif

#if defined(CONFIG_ARM_SMMU) || defined(CONFIG_IOMMU)
/* set the mapping bit */
guest_vspace->done_mapping = 1;
cspacepath_t orig_path;
/* duplicate the cap so we can do a mapping */
vka_cspace_make_path(guest_vspace->vspace_data.vka, cap, &orig_path);
/* map into all the io spaces */
for (int i = 0; i < guest_vspace->num_iospaces; i++) {
cspacepath_t new_path;
error = vka_cspace_alloc_path(guest_vspace->vspace_data.vka, &new_path);
if (error) {
ZF_LOGE("Failed to allocate cslot to duplicate frame cap");
Expand Down Expand Up @@ -98,12 +112,27 @@ void guest_vspace_unmap(vspace_t *vspace, void *vaddr, size_t num_pages, size_t
* This can be done in a single call as mappings are contiguous in this vspace. */
sel4utils_unmap_pages(vspace, vaddr, num_pages, size_bits, vka);

#if defined(CONFIG_ARM_SMMU) || defined(CONFIG_IOMMU)
/* Each page must be unmapped individually from the vmm vspace, as mappings are not
* necessarily host-virtually contiguous. */
size_t page_size = BIT(size_bits);
for (int i = 0; i < num_pages; i++) {
void *page_vaddr = (void *)(vaddr + i * page_size);
#ifdef CONFIG_LIB_SEL4VM_USE_TRANSLATION_VSPACE
/* look up vaddr in vmm vspace by consulting entry in translation vspace */
void *vmm_vaddr = (void *)vspace_get_cookie(&guest_vspace->translation_vspace, page_vaddr);

/* remove mapping from vmm vspace */
vspace_unmap_pages(&guest_vspace->vmm_vspace, vmm_vaddr, 1 /* num pages */, size_bits, vka);

/* remove mapping from translation vspace */
error = clear_entries(&guest_vspace->translation_vspace, (uintptr_t)page_vaddr, size_bits);
if (error) {
ZF_LOGE("Failed to clear translation information");
return;
}
#endif

#if defined(CONFIG_ARM_SMMU) || defined(CONFIG_IOMMU)
/* Unmap the vaddr from each iospace, freeing the cslots used to store the
* copy of the frame cap. */
for (int i = 0; i < guest_vspace->num_iospaces; i++) {
Expand Down Expand Up @@ -132,8 +161,8 @@ void guest_vspace_unmap(vspace_t *vspace, void *vaddr, size_t num_pages, size_t
return;
}
}
}
#endif
}
}

int vm_guest_add_iospace(vm_t *vm, vspace_t *loader, seL4_CPtr iospace)
Expand Down Expand Up @@ -173,6 +202,15 @@ int vm_init_guest_vspace(vspace_t *loader, vspace_t *vmm, vspace_t *new_vspace,
vspace->num_iospaces = 0;
vspace->iospaces = malloc(0);
assert(vspace->iospaces);
#ifdef CONFIG_LIB_SEL4VM_USE_TRANSLATION_VSPACE
vspace->vmm_vspace = *vmm;
error = sel4utils_get_vspace(loader, &vspace->translation_vspace, &vspace->translation_vspace_data, vka, page_directory,
NULL, NULL);
if (error) {
ZF_LOGE("Failed to create translation vspace");
return error;
}
#endif
error = sel4utils_get_empty_vspace_with_map(loader, new_vspace, &vspace->vspace_data, vka, page_directory, NULL, NULL,
guest_vspace_map);
if (error) {
Expand Down
30 changes: 30 additions & 0 deletions libsel4vm/src/guest_vspace.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,35 @@
#include <vspace/vspace.h>
#include <vka/vka.h>

#include <sel4utils/vspace.h>

#include <sel4vm/gen_config.h>

typedef struct guest_iospace {
seL4_CPtr iospace;
struct sel4utils_alloc_data iospace_vspace_data;
vspace_t iospace_vspace;
} guest_iospace_t;

typedef struct guest_vspace {
/* We abuse struct ordering and this member MUST be the first
* thing in the struct */
struct sel4utils_alloc_data vspace_data;
#ifdef CONFIG_LIB_SEL4VM_USE_TRANSLATION_VSPACE
/* additional vspace to place all mappings into. We will maintain
* a translation between this and the guest */
vspace_t vmm_vspace;
/* use a vspace implementation as a sparse data structure to track
* the translation from guest to vmm */
struct sel4utils_alloc_data translation_vspace_data;
vspace_t translation_vspace;
#endif
/* debug flag for checking if we add io spaces late */
int done_mapping;
int num_iospaces;
guest_iospace_t **iospaces;
} guest_vspace_t;


/* Constructs a vspace that will duplicate mappings between a page directory and several IO spaces */
int vm_init_guest_vspace(vspace_t *loader, vspace_t *vmm, vspace_t *new_vspace, vka_t *vka, seL4_CPtr page_directory);