blob: 63f15b4ab40a26171c589424617162f1c0e9be5b [file] [log] [blame]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
/* see sel4utils/vspace.h for details */
#include <autoconf.h>
#include <sel4utils/gen_config.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sel4utils/vspace.h>
#include <sel4utils/page.h>
#include <sel4utils/vspace_internal.h>
#include <vka/capops.h>
#include <utils/util.h>
void *create_level(vspace_t *vspace, size_t size)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
/* We need a level in the bootstrapper vspace */
if (data->bootstrap == NULL) {
return bootstrap_create_level(vspace, size);
}
/* Otherwise we allocate our level out of the bootstrapper vspace -
* which is where bookkeeping is mapped */
void *level = vspace_new_pages(data->bootstrap, seL4_AllRights,
size / PAGE_SIZE_4K, seL4_PageBits);
if (level == NULL) {
return NULL;
}
memset(level, 0, size);
return level;
}
/* check that vaddr is actually in the reservation */
static int check_reservation_bounds(sel4utils_res_t *reservation, uintptr_t start, uintptr_t end)
{
return start >= reservation->start &&
end <= reservation->end;
}
static int check_reservation(vspace_mid_level_t *top_level, sel4utils_res_t *reservation, uintptr_t start,
uintptr_t end)
{
return check_reservation_bounds(reservation, start, end) &&
is_reserved_range(top_level, start, end);
}
static void insert_reservation(sel4utils_alloc_data_t *data, sel4utils_res_t *reservation)
{
assert(data != NULL);
assert(reservation != NULL);
reservation->next = NULL;
/* insert at head */
if (data->reservation_head == NULL || reservation->start > data->reservation_head->start) {
reservation->next = data->reservation_head;
data->reservation_head = reservation;
return;
}
/* insert elsewhere */
sel4utils_res_t *prev = data->reservation_head;
sel4utils_res_t *current = prev->next;
while (current != NULL) {
/* insert in the middle */
if (reservation->start > current->start) {
reservation->next = current;
prev->next = reservation;
return;
}
prev = current;
current = current->next;
}
/* insert at the end */
prev->next = reservation;
}
static void remove_reservation(sel4utils_alloc_data_t *data, sel4utils_res_t *reservation)
{
/* remove head */
if (reservation == data->reservation_head) {
data->reservation_head = data->reservation_head->next;
reservation->next = NULL;
return;
}
sel4utils_res_t *prev = data->reservation_head;
sel4utils_res_t *current = prev->next;
while (current != NULL) {
/* remove middle */
if (current == reservation) {
prev->next = reservation->next;
reservation->next = NULL;
return;
}
prev = current;
current = current->next;
}
/* remove tail */
prev->next = NULL;
reservation->next = NULL;
}
static void perform_reservation(vspace_t *vspace, sel4utils_res_t *reservation, uintptr_t vaddr, size_t bytes,
seL4_CapRights_t rights, int cacheable)
{
assert(reservation != NULL);
UNUSED int error;
reservation->start = ROUND_DOWN(vaddr, PAGE_SIZE_4K);
reservation->end = ROUND_UP(vaddr + bytes, PAGE_SIZE_4K);
reservation->rights = rights;
reservation->cacheable = cacheable;
error = reserve_entries_range(vspace, reservation->start, reservation->end, true);
/* only support to reserve things that we've checked that we can */
assert(error == seL4_NoError);
/* insert the reservation ordered */
insert_reservation(get_alloc_data(vspace), reservation);
}
int sel4utils_map_page_pd(vspace_t *vspace, seL4_CPtr cap, void *vaddr, seL4_CapRights_t rights,
int cacheable, size_t size_bits)
{
vka_object_t objects[VSPACE_MAP_PAGING_OBJECTS];
int num = VSPACE_MAP_PAGING_OBJECTS;
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
int error = sel4utils_map_page(data->vka, data->vspace_root, cap, vaddr,
rights, cacheable, objects, &num);
if (error) {
/* everything has gone to hell. Do no clean up. */
ZF_LOGE("Error mapping pages, bailing: %d", error);
return -1;
}
for (int i = 0; i < num; i++) {
vspace_maybe_call_allocated_object(vspace, objects[i]);
}
return seL4_NoError;
}
#ifdef CONFIG_VTX
int sel4utils_map_page_ept(vspace_t *vspace, seL4_CPtr cap, void *vaddr, seL4_CapRights_t rights,
int cacheable, size_t size_bits)
{
struct sel4utils_alloc_data *data = get_alloc_data(vspace);
vka_object_t pagetable = {0};
vka_object_t pagedir = {0};
vka_object_t pdpt = {0};
int error = sel4utils_map_ept_page(data->vka, data->vspace_root, cap,
(seL4_Word) vaddr, rights, cacheable, size_bits, &pagetable, &pagedir, &pdpt);
if (error) {
ZF_LOGE("Error mapping pages, bailing\n");
return -1;
}
if (pagetable.cptr != 0) {
vspace_maybe_call_allocated_object(vspace, pagetable);
pagetable.cptr = 0;
}
if (pagedir.cptr != 0) {
vspace_maybe_call_allocated_object(vspace, pagedir);
pagedir.cptr = 0;
}
if (pdpt.cptr != 0) {
vspace_maybe_call_allocated_object(vspace, pdpt);
pdpt.cptr = 0;
}
return seL4_NoError;
}
#endif /* CONFIG_VTX */
#ifdef CONFIG_IOMMU
int sel4utils_map_page_iommu(vspace_t *vspace, seL4_CPtr cap, void *vaddr, seL4_CapRights_t rights,
int cacheable, size_t size_bits)
{
struct sel4utils_alloc_data *data = get_alloc_data(vspace);
int num_pts = 0;
/* The maximum number of page table levels current Intel hardware implements is 6 */
vka_object_t pts[7];
int error = sel4utils_map_iospace_page(data->vka, data->vspace_root, cap,
(seL4_Word) vaddr, rights, cacheable, size_bits, pts, &num_pts);
if (error) {
ZF_LOGE("Error mapping pages, bailing");
return -1;
}
for (int i = 0; i < num_pts; i++) {
vspace_maybe_call_allocated_object(vspace, pts[i]);
}
return seL4_NoError;
}
#endif /* CONFIG_IOMMU */
static int map_page(vspace_t *vspace, seL4_CPtr cap, void *vaddr, seL4_CapRights_t rights,
int cacheable, size_t size_bits)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
return data->map_page(vspace, cap, vaddr, rights, cacheable, size_bits);
}
static sel4utils_res_t *find_reserve(sel4utils_alloc_data_t *data, uintptr_t vaddr)
{
sel4utils_res_t *current = data->reservation_head;
while (current != NULL) {
if (vaddr >= current->start && vaddr < current->end) {
return current;
}
current = current->next;
}
return NULL;
}
static void *find_range(sel4utils_alloc_data_t *data, size_t num_pages, size_t size_bits)
{
/* look for a contiguous range that is free.
* We use first-fit with the optimisation that we store
* a pointer to the last thing we freed/allocated */
size_t contiguous = 0;
uintptr_t start = ALIGN_UP(data->last_allocated, SIZE_BITS_TO_BYTES(size_bits));
uintptr_t current = start;
assert(IS_ALIGNED(start, size_bits));
while (contiguous < num_pages) {
bool available = is_available(data->top_level, current, size_bits);
current += SIZE_BITS_TO_BYTES(size_bits);
if (available) {
/* keep going! */
contiguous++;
} else {
/* reset start and try again */
start = current;
contiguous = 0;
}
if (current >= KERNEL_RESERVED_START) {
ZF_LOGE("Out of virtual memory");
return NULL;
}
}
data->last_allocated = current;
return (void *) start;
}
static int map_pages_at_vaddr(vspace_t *vspace, seL4_CPtr caps[], uintptr_t cookies[],
void *vaddr, size_t num_pages,
size_t size_bits, seL4_CapRights_t rights, int cacheable)
{
int error = seL4_NoError;
for (int i = 0; i < num_pages && error == seL4_NoError; i++) {
error = map_page(vspace, caps[i], vaddr, rights, cacheable, size_bits);
if (error == seL4_NoError) {
uintptr_t cookie = cookies == NULL ? 0 : cookies[i];
error = update_entries(vspace, (uintptr_t) vaddr, caps[i], size_bits, cookie);
vaddr = (void *)((uintptr_t) vaddr + (BIT(size_bits)));
}
}
return error;
}
static int new_pages_at_vaddr(vspace_t *vspace, void *vaddr, size_t num_pages, size_t size_bits,
seL4_CapRights_t rights, int cacheable, bool can_use_dev)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
int i;
int error = seL4_NoError;
void *start_vaddr = vaddr;
for (i = 0; i < num_pages; i++) {
vka_object_t object;
if (vka_alloc_frame_maybe_device(data->vka, size_bits, can_use_dev, &object) != 0) {
/* abort! */
ZF_LOGE("Failed to allocate page number: %d out of %zu", i, num_pages);
error = seL4_NotEnoughMemory;
break;
}
error = map_page(vspace, object.cptr, vaddr, rights, cacheable, size_bits);
if (error == seL4_NoError) {
error = update_entries(vspace, (uintptr_t) vaddr, object.cptr, size_bits, object.ut);
vaddr = (void *)((uintptr_t) vaddr + (BIT(size_bits)));
} else {
vka_free_object(data->vka, &object);
break;
}
}
if (i < num_pages) {
/* we failed, clean up successfully allocated pages */
sel4utils_unmap_pages(vspace, start_vaddr, i, size_bits, data->vka);
}
return error;
}
/* VSPACE INTERFACE FUNCTIONS */
int sel4utils_map_pages_at_vaddr(vspace_t *vspace, seL4_CPtr caps[], uintptr_t cookies[], void *vaddr,
size_t num_pages, size_t size_bits, reservation_t reservation)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
sel4utils_res_t *res = reservation_to_res(reservation);
if (!sel4_valid_size_bits(size_bits)) {
ZF_LOGE("Invalid size_bits %zu", size_bits);
return -1;
}
if (!check_reservation(data->top_level, res, (uintptr_t) vaddr, (uintptr_t)vaddr + num_pages * BIT(size_bits))) {
ZF_LOGE("Invalid reservation");
return -1;
}
if (res->rights_deferred) {
ZF_LOGE("Reservation has no rights associated with it");
return -1;
}
return map_pages_at_vaddr(vspace, caps, cookies, vaddr, num_pages, size_bits,
res->rights, res->cacheable);
}
int sel4utils_deferred_rights_map_pages_at_vaddr(vspace_t *vspace, seL4_CPtr caps[], uintptr_t cookies[], void *vaddr,
size_t num_pages, size_t size_bits,
seL4_CapRights_t rights, reservation_t reservation)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
sel4utils_res_t *res = reservation_to_res(reservation);
if (!sel4_valid_size_bits(size_bits)) {
ZF_LOGE("Invalid size_bits %zu", size_bits);
return -1;
}
if (!check_reservation(data->top_level, res, (uintptr_t) vaddr, (uintptr_t)vaddr + num_pages * BIT(size_bits))) {
ZF_LOGE("Invalid reservation");
return -1;
}
if (!res->rights_deferred) {
ZF_LOGE("Invalid rights: rights already given to reservation");
return -1;
}
return map_pages_at_vaddr(vspace, caps, cookies, vaddr, num_pages, size_bits,
rights, res->cacheable);
}
void *sel4utils_map_pages(vspace_t *vspace, seL4_CPtr caps[], uintptr_t cookies[],
seL4_CapRights_t rights, size_t num_pages, size_t size_bits,
int cacheable)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
int error;
void *ret_vaddr;
assert(num_pages > 0);
ret_vaddr = find_range(data, num_pages, size_bits);
if (ret_vaddr == NULL) {
return NULL;
}
error = map_pages_at_vaddr(vspace, caps, cookies,
ret_vaddr, num_pages, size_bits,
rights, cacheable);
if (error != 0) {
if (clear_entries(vspace, (uintptr_t)ret_vaddr, size_bits) != 0) {
ZF_LOGE("FATAL: Failed to clear VMM metadata for vmem @0x%p, %lu pages.",
ret_vaddr, BIT(size_bits));
/* This is probably cause for a panic, but continue anyway. */
}
return NULL;
}
return ret_vaddr;
}
seL4_CPtr sel4utils_get_cap(vspace_t *vspace, void *vaddr)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
seL4_CPtr cap = get_cap(data->top_level, (uintptr_t) vaddr);
if (cap == RESERVED) {
cap = 0;
}
return cap;
}
uintptr_t sel4utils_get_cookie(vspace_t *vspace, void *vaddr)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
return get_cookie(data->top_level, (uintptr_t) vaddr);
}
void sel4utils_unmap_pages(vspace_t *vspace, void *vaddr, size_t num_pages, size_t size_bits, vka_t *vka)
{
uintptr_t v = (uintptr_t) vaddr;
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
sel4utils_res_t *reserve = find_reserve(data, v);
if (!sel4_valid_size_bits(size_bits)) {
ZF_LOGE("Invalid size_bits %zu", size_bits);
return;
}
if (vka == VSPACE_FREE) {
vka = data->vka;
}
for (int i = 0; i < num_pages; i++) {
seL4_CPtr cap = get_cap(data->top_level, v);
/* unmap */
if (cap != 0) {
int error = seL4_ARCH_Page_Unmap(cap);
if (error != seL4_NoError) {
ZF_LOGE("Failed to unmap page at vaddr %p", vaddr);
}
}
if (vka) {
cspacepath_t path;
vka_cspace_make_path(vka, cap, &path);
vka_cnode_delete(&path);
vka_cspace_free(vka, cap);
if (sel4utils_get_cookie(vspace, vaddr)) {
vka_utspace_free(vka, kobject_get_type(KOBJECT_FRAME, size_bits),
size_bits, sel4utils_get_cookie(vspace, vaddr));
}
}
if (reserve == NULL) {
clear_entries(vspace, v, size_bits);
} else {
reserve_entries(vspace, v, size_bits);
}
assert(get_cap(data->top_level, v) != cap);
assert(get_cookie(data->top_level, v) == 0);
v += (BIT(size_bits));
vaddr = (void *) v;
}
}
int sel4utils_new_pages_at_vaddr(vspace_t *vspace, void *vaddr, size_t num_pages,
size_t size_bits, reservation_t reservation, bool can_use_dev)
{
struct sel4utils_alloc_data *data = get_alloc_data(vspace);
sel4utils_res_t *res = reservation_to_res(reservation);
if (!check_reservation(data->top_level, res, (uintptr_t) vaddr, (uintptr_t)vaddr + num_pages * BIT(size_bits))) {
ZF_LOGE("Range for vaddr %p with %"PRIuPTR" 4k pages not reserved!", vaddr, num_pages);
return -1;
}
return new_pages_at_vaddr(vspace, vaddr, num_pages, size_bits, res->rights, res->cacheable, can_use_dev);
}
void *sel4utils_new_pages(vspace_t *vspace, seL4_CapRights_t rights,
size_t num_pages, size_t size_bits)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
int error;
void *ret_vaddr;
assert(num_pages > 0);
ret_vaddr = find_range(data, num_pages, size_bits);
if (ret_vaddr == NULL) {
return NULL;
}
/* Since sel4utils_new_pages() is an implementation of vspace_new_pages(),
* it should ideally be preferring to allocate device untypeds and leaving
* the non-device untypeds for VKA to use when it's allocating kernel objects.
*
* Unfortunately it currently has to prefer to allocate non-device untypeds
* to maintain compatibility with code that uses it incorrectly, such as
* code that calls vspace_new_pages() to allocate an IPC buffer.
*/
error = new_pages_at_vaddr(vspace, ret_vaddr, num_pages, size_bits, rights,
(int)true, false);
if (error != 0) {
if (clear_entries(vspace, (uintptr_t)ret_vaddr, size_bits) != 0) {
ZF_LOGE("FATAL: Failed to clear VMM metadata for vmem @0x%p, %lu pages.",
ret_vaddr, BIT(size_bits));
/* This is probably cause for a panic, but continue anyway. */
}
return NULL;
}
return ret_vaddr;
}
int sel4utils_reserve_range_no_alloc_aligned(vspace_t *vspace, sel4utils_res_t *reservation,
size_t size, size_t size_bits, seL4_CapRights_t rights, int cacheable, void **result)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
void *vaddr = find_range(data, BYTES_TO_SIZE_BITS_PAGES(size, size_bits), size_bits);
if (vaddr == NULL) {
return -1;
}
*result = vaddr;
reservation->malloced = 0;
reservation->rights_deferred = false;
perform_reservation(vspace, reservation, (uintptr_t) vaddr, size, rights, cacheable);
return 0;
}
int sel4utils_reserve_range_no_alloc(vspace_t *vspace, sel4utils_res_t *reservation, size_t size,
seL4_CapRights_t rights, int cacheable, void **result)
{
return sel4utils_reserve_range_no_alloc_aligned(vspace, reservation, size, seL4_PageBits,
rights, cacheable, result);
}
reservation_t sel4utils_reserve_range_aligned(vspace_t *vspace, size_t bytes, size_t size_bits, seL4_CapRights_t rights,
int cacheable, void **result)
{
reservation_t reservation = {
.res = NULL,
};
if (!sel4_valid_size_bits(size_bits)) {
ZF_LOGE("Invalid size bits %zu", size_bits);
return reservation;
}
sel4utils_res_t *res = malloc(sizeof(sel4utils_res_t));
if (res == NULL) {
ZF_LOGE("Malloc failed");
return reservation;
}
reservation.res = res;
int error = sel4utils_reserve_range_no_alloc_aligned(vspace, res, bytes, size_bits, rights, cacheable, result);
if (error) {
free(reservation.res);
reservation.res = NULL;
}
res->malloced = 1;
return reservation;
}
int sel4utils_reserve_range_at_no_alloc(vspace_t *vspace, sel4utils_res_t *reservation, void *vaddr,
size_t size, seL4_CapRights_t rights, int cacheable)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
if (!is_available_range(data->top_level, (uintptr_t) vaddr, (uintptr_t)vaddr + size)) {
ZF_LOGE("Range not available at %p, size %p", vaddr, (void *)size);
return -1;
}
reservation->malloced = 0;
reservation->rights_deferred = false;
perform_reservation(vspace, reservation, (uintptr_t) vaddr, size, rights, cacheable);
return 0;
}
reservation_t sel4utils_reserve_range_at(vspace_t *vspace, void *vaddr, size_t size, seL4_CapRights_t
rights, int cacheable)
{
reservation_t reservation;
reservation.res = malloc(sizeof(sel4utils_res_t));
if (reservation.res == NULL) {
ZF_LOGE("Malloc failed");
return reservation;
}
int error = sel4utils_reserve_range_at_no_alloc(vspace, reservation.res, vaddr, size, rights, cacheable);
if (error) {
free(reservation.res);
reservation.res = NULL;
} else {
((sel4utils_res_t *)reservation.res)->malloced = 1;
}
return reservation;
}
reservation_t sel4utils_reserve_deferred_rights_range_at(vspace_t *vspace, void *vaddr, size_t size, int cacheable)
{
reservation_t reservation = sel4utils_reserve_range_at(vspace, vaddr, size, seL4_NoRights, cacheable);
if (reservation.res != NULL) {
((sel4utils_res_t *)reservation.res)->rights_deferred = true;
}
return reservation;
}
void sel4utils_free_reservation(vspace_t *vspace, reservation_t reservation)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
sel4utils_res_t *res = reservation.res;
clear_entries_range(vspace, res->start, res->end, true);
remove_reservation(data, res);
if (res->malloced) {
free(reservation.res);
}
}
void sel4utils_free_reservation_by_vaddr(vspace_t *vspace, void *vaddr)
{
reservation_t reservation;
reservation.res = find_reserve(get_alloc_data(vspace), (uintptr_t) vaddr);
sel4utils_free_reservation(vspace, reservation);
}
int sel4utils_move_resize_reservation(vspace_t *vspace, reservation_t reservation, void *vaddr,
size_t bytes)
{
assert(reservation.res != NULL);
sel4utils_res_t *res = reservation.res;
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
uintptr_t new_start = ROUND_DOWN((uintptr_t) vaddr, PAGE_SIZE_4K);
uintptr_t new_end = ROUND_UP(((uintptr_t)(vaddr)) + bytes, PAGE_SIZE_4K);
uintptr_t v = 0;
/* Sanity checks that newly asked reservation space is available. */
if (new_start < res->start) {
if (!is_available_range(data->top_level, new_start, res->start)) {
return -1;
}
}
if (new_end > res->end) {
if (!is_available_range(data->top_level, res->end, new_end)) {
return -2;
}
}
for (v = new_start; v < new_end; v += PAGE_SIZE_4K) {
if (v < res->start || v >= res->end) {
/* Any outside the reservation must be unreserved. */
int error UNUSED = reserve_entries_range(vspace, v, v + PAGE_SIZE_4K, true);
/* Should not cause any errors as we have just checked the regions are free. */
assert(!error);
} else {
v = res->end - PAGE_SIZE_4K;
}
}
for (v = res->start; v < res->end; v += PAGE_SIZE_4K) {
if (v < new_start || v >= new_end) {
/* Clear any regions that aren't reserved by the new region any more. */
if (get_cap(data->top_level, v) == RESERVED) {
clear_entries_range(vspace, v, v + PAGE_SIZE_4K, true);
}
} else {
v = new_end - PAGE_SIZE_4K;
}
}
bool need_reinsert = false;
if (res->start != new_start) {
need_reinsert = true;
}
res->start = new_start;
res->end = new_end;
/* We may need to re-insert the reservation into the list to keep it sorted by start address. */
if (need_reinsert) {
remove_reservation(data, res);
insert_reservation(data, res);
}
return 0;
}
seL4_CPtr sel4utils_get_root(vspace_t *vspace)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
return data->vspace_root;
}
static void free_page(vspace_t *vspace, vka_t *vka, uintptr_t vaddr)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
vspace_mid_level_t *level = data->top_level;
/* see if we should free the thing here or not */
uintptr_t cookie = get_cookie(level, vaddr);
int num_4k_entries = 1;
if (cookie != 0) {
/* walk along and see just how big this page is */
uintptr_t test_vaddr = vaddr + PAGE_SIZE_4K;
while (get_cookie(level, test_vaddr) == cookie) {
test_vaddr += PAGE_SIZE_4K;
num_4k_entries++;
}
sel4utils_unmap_pages(vspace, (void *)vaddr, 1, PAGE_BITS_4K * num_4k_entries, vka);
}
}
static void free_pages_at_level(vspace_t *vspace, vka_t *vka, int table_level, uintptr_t vaddr)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
vspace_mid_level_t *level = data->top_level;
/* walk down to the level that we want */
for (int i = VSPACE_NUM_LEVELS - 1; i > table_level && i > 1; i--) {
int index = INDEX_FOR_LEVEL(vaddr, i);
switch (level->table[index]) {
case RESERVED:
case EMPTY:
return;
}
level = (vspace_mid_level_t *)level->table[index];
}
if (table_level == 0) {
int index = INDEX_FOR_LEVEL(vaddr, 1);
switch (level->table[index]) {
case RESERVED:
case EMPTY:
return;
}
vspace_bottom_level_t *bottom = (vspace_bottom_level_t *)level->table[index];
index = INDEX_FOR_LEVEL(vaddr, 0);
if (bottom->cap[index] != EMPTY && bottom->cap[index] != RESERVED) {
free_page(vspace, vka, vaddr);
}
} else {
int index = INDEX_FOR_LEVEL(vaddr, table_level);
switch (level->table[index]) {
case RESERVED:
case EMPTY:
return;
}
/* recurse to the sub level */
for (int j = 0; j < VSPACE_LEVEL_SIZE; j++) {
free_pages_at_level(vspace, vka,
table_level - 1,
vaddr + j * BYTES_FOR_LEVEL(table_level - 1));
}
vspace_unmap_pages(data->bootstrap, (void *)level->table[index],
(table_level == 1 ? sizeof(vspace_bottom_level_t) : sizeof(vspace_mid_level_t)) / PAGE_SIZE_4K, PAGE_BITS_4K,
VSPACE_FREE);
}
}
void sel4utils_tear_down(vspace_t *vspace, vka_t *vka)
{
sel4utils_alloc_data_t *data = get_alloc_data(vspace);
if (data->bootstrap == NULL) {
ZF_LOGE("Not implemented: sel4utils cannot currently tear down a self-bootstrapped vspace\n");
return;
}
if (vka == VSPACE_FREE) {
vka = data->vka;
}
/* free all the reservations */
while (data->reservation_head != NULL) {
reservation_t res = { .res = data->reservation_head };
sel4utils_free_reservation(vspace, res);
}
/* walk each level and find any pages / large pages */
if (data->top_level) {
for (int i = 0; i < BIT(VSPACE_LEVEL_BITS); i++) {
free_pages_at_level(vspace, vka, VSPACE_NUM_LEVELS - 1, BYTES_FOR_LEVEL(VSPACE_NUM_LEVELS - 1) * i);
}
vspace_unmap_pages(data->bootstrap, data->top_level, sizeof(vspace_mid_level_t) / PAGE_SIZE_4K, PAGE_BITS_4K,
VSPACE_FREE);
}
}
int sel4utils_share_mem_at_vaddr(vspace_t *from, vspace_t *to, void *start, int num_pages,
size_t size_bits, void *vaddr, reservation_t reservation)
{
int error = 0; /* no error */
sel4utils_alloc_data_t *from_data = get_alloc_data(from);
sel4utils_alloc_data_t *to_data = get_alloc_data(to);
cspacepath_t from_path, to_path;
int page;
sel4utils_res_t *res = reservation_to_res(reservation);
if (!sel4_valid_size_bits(size_bits)) {
ZF_LOGE("Invalid size bits %zu", size_bits);
return -1;
}
/* go through, page by page, and duplicate the page cap into the to cspace and
* map it into the to vspace */
size_t size_bytes = 1 << size_bits;
for (page = 0; page < num_pages; page++) {
uintptr_t from_vaddr = (uintptr_t) start + page * size_bytes;
uintptr_t to_vaddr = (uintptr_t) vaddr + (uintptr_t) page * size_bytes;
/* get the frame cap to be copied */
seL4_CPtr cap = get_cap(from_data->top_level, from_vaddr);
if (cap == seL4_CapNull) {
ZF_LOGE("Cap not present in from vspace to copy, vaddr %"PRIuPTR, from_vaddr);
error = -1;
break;
}
/* create a path to the cap */
vka_cspace_make_path(from_data->vka, cap, &from_path);
/* allocate a path to put the copy in the destination */
error = vka_cspace_alloc_path(to_data->vka, &to_path);
if (error) {
ZF_LOGE("Failed to allocate slot in to cspace, error: %d", error);
break;
}
/* copy the frame cap into the to cspace */
error = vka_cnode_copy(&to_path, &from_path, res->rights);
if (error) {
ZF_LOGE("Failed to copy cap, error %d\n", error);
break;
}
/* now finally map the page */
error = map_page(to, to_path.capPtr, (void *) to_vaddr, res->rights, res->cacheable, size_bits);
if (error) {
ZF_LOGE("Failed to map page into target vspace at vaddr %"PRIuPTR, to_vaddr);
break;
}
update_entries(to, to_vaddr, to_path.capPtr, size_bits, 0);
}
if (error) {
/* we didn't finish, undo any pages we did map */
vspace_unmap_pages(to, vaddr, page, size_bits, VSPACE_FREE);
}
return error;
}
uintptr_t sel4utils_get_paddr(vspace_t *vspace, void *vaddr, seL4_Word type, seL4_Word size_bits)
{
vka_t *vka = get_alloc_data(vspace)->vka;
return vka_utspace_paddr(vka, vspace_get_cookie(vspace, vaddr), type, size_bits);
}