Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Xen core platform stack #23

Merged
merged 11 commits into from
Oct 15, 2020
17 changes: 17 additions & 0 deletions lib/bindings/bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define __XEN_BINDINGS_H__

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "xen/xen.h"

Expand Down Expand Up @@ -118,4 +119,20 @@ static inline uint16_t atomic_sync_cmpxchgw(volatile uint16_t *ptr,
#error Not implemented
#endif /* __x86_64__ */

/*
* Accessor for retrieving guest-virtual memory range suitable for importing
* grant mappings from Solo5.
*/
void solo5__xen_get_gntmap_area(uint64_t *addr, size_t *size);

/*
* Bitmap allocator for virtual memory used for importing grant mappings.
*/
struct bmap_allocator;
typedef struct bmap_allocator bmap_allocator_t;

bmap_allocator_t *bmap_init(uint64_t start_addr, size_t n_pages);
void *bmap_alloc(bmap_allocator_t *alloc, size_t n);
void bmap_free(bmap_allocator_t *alloc, void *addr, size_t n);

#endif /* __XEN_BINDINGS_H__ */
251 changes: 251 additions & 0 deletions lib/bindings/bmap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
/*
* Copyright (c) 2020 Martin Lucina <[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include <stddef.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#ifndef TEST_STANDALONE
#include "bindings.h"
#else
#define PAGE_SIZE 4096
#endif

/*
* This is a simple bitmap allocator for virtual memory addresses. Worst-case
* performance for bmap_alloc() is O(n), where n = total_pages / 8. bmap_free()
* is O(1) but requires the caller to keep the size of the allocated block.
*/
struct bmap_allocator {
long *bmap; /* 1 bit per page; 1=free, 0=used */
size_t bmap_size; /* # of words in bmap[] */
uint64_t start_addr; /* starting virtual memory address */
};
typedef struct bmap_allocator bmap_allocator_t;

#define BPW (sizeof(long) * 8)
_Static_assert(sizeof(long) == 8, "long must be 64 bits");

/*
* Returns 0-based index of first set bit in (bmap[]), starting with the bit
* index (at), or -1 if none found and end of (bmap[]) was reached.
*/
static int ffs_at(long *bmap, size_t bmap_size, int at)
{
int word = at / BPW;
int shift = at % BPW;
int bit = 0;
int i;

for (i = word; i < bmap_size; i++) {
if (i == word)
/* (at) is not on a word boundary; shift so we can use ffsl */
bit = __builtin_ffsl(bmap[i] >> shift);
else
bit = __builtin_ffsl(bmap[i]);
if (bit)
break;
}

if (bit) {
if (i == word)
/* Restore previous shift if any */
bit += shift;
return (i * BPW) + (bit - 1);
}
else
return -1;
}

/*
* Returns 0-based index of first clear bit in (bmap[]), starting with the bit
* index (at), or -1 if none found and end of (bmap[]) was reached.
*/
static int ffc_at(long *bmap, size_t bmap_size, int at)
{
int word = at / BPW;
int shift = at % BPW;
int bit = 0;
int i;

for (i = word; i < bmap_size; i++) {
if (i == word)
/* (at) is not on a word boundary; shift so we can use ffsl */
bit = __builtin_ffsl(~bmap[i] >> shift);
else
bit = __builtin_ffsl(~bmap[i]);
if (bit)
break;
}

if (bit) {
if (i == word)
/* Restore previous shift if any */
bit += shift;
return (i * BPW) + (bit - 1);
}
else
return -1;
}

/*
* Set (n) bits in (bmap[]) at 0-based bit index (at).
*/
static void setn_at(long *bmap, size_t bmap_size, int at, int n)
{
assert((at + n - 1) < (bmap_size * BPW));
while (n > 0) {
n -= 1;
bmap[((at + n) / BPW)] |= (1UL << ((at + n) % BPW));
}
}

/*
* Clear (n) bits in (bmap[]) at 0-based bit index (at).
*/
static void clearn_at(long *bmap, size_t bmap_size, int at, int n)
{
assert((at + n - 1) < (bmap_size * BPW));
while (n > 0) {
n -= 1;
bmap[((at + n) / BPW)] &= ~(1UL << ((at + n) % BPW));
}
}

/*
* Allocate (n) pages from (alloc), returns a memory address or NULL if no
* space found.
*/
void *bmap_alloc(bmap_allocator_t *alloc, size_t n)
{
int a = 0, b = 0;
size_t bmap_bits = alloc->bmap_size * BPW;

/*
* Allocating 0 pages is not allowed.
*/
assert(n >= 1);

while (1) {
/*
* Look for the first free page starting at (b), initially 0.
*/
a = ffs_at(alloc->bmap, alloc->bmap_size, b);
if (a < 0)
return NULL;
/*
* Look for the first used page after the found free page.
*/
b = ffc_at(alloc->bmap, alloc->bmap_size, a);
if (b < 0)
/*
* Nothing found; all remaining pages from a..bmap_bits are free.
*/
b = bmap_bits;
/*
* Is the block big enough? If yes, mark as used (0) and return it.
*/
if (b - a >= n) {
clearn_at(alloc->bmap, alloc->bmap_size, a, n);
return (void *)(alloc->start_addr + (a * PAGE_SIZE));
}
/*
* Stop the search if we hit the end of bmap[] and did not find a large
* enough block.
*/
if (b == bmap_bits)
return NULL;
/*
* If we got here, loop with (b) set to the last seen used page.
*/
}
}

/*
* Free (n) pages at (addr) from (alloc).
*/
void bmap_free(bmap_allocator_t *alloc, void *addr, size_t n)
{
/*
* Verify that:
* addr is page-aligned
* addr is within the range given to alloc
* n is at least 1; the maxiumum size of n is checked in clearn_at().
*/
assert(((uintptr_t)addr & (PAGE_SIZE - 1)) == 0);
assert((uintptr_t)addr >= alloc->start_addr);
assert(n >= 1);

int a = ((uintptr_t)addr - alloc->start_addr) / PAGE_SIZE;
setn_at(alloc->bmap, alloc->bmap_size, a, n);
}

/*
* Initialise the allocator to use (n_pages) at (start_addr).
*/
bmap_allocator_t *bmap_init(uint64_t start_addr, size_t n_pages)
{
bmap_allocator_t *alloc = malloc(sizeof (bmap_allocator_t));
assert(alloc != NULL);
/*
* n_pages must be a multiple of BPW.
*/
assert((n_pages % BPW) == 0);
alloc->bmap_size = n_pages / BPW;
alloc->bmap = malloc(alloc->bmap_size * sizeof(long));
assert(alloc->bmap);
alloc->start_addr = start_addr;
/*
* All pages are initially free; set all bits in bmap[].
*/
memset(alloc->bmap, 0xff, alloc->bmap_size * sizeof(long));

return alloc;
}

#ifdef TEST_STANDALONE
/*
* For standalone testing of the algorithm.
*/

#include <stdio.h>

int main(int argc, char *argv[])
{
if (argc != 2) {
printf("need allocation size\n");
return 1;
}

int n = atoi(argv[1]);
bmap_allocator_t *alloc = bmap_init(0x100000, 4096);

char *a;
int c = 0;
do {
a = bmap_alloc(alloc, n);
if (a)
printf ("%d: %p - %p\n", ++c, a, a + (n * PAGE_SIZE) - 1);
} while (a);

/* Keep valgrind happy when testing standalone. */
free(alloc->bmap);
free(alloc);
}

#endif
32 changes: 25 additions & 7 deletions lib/bindings/gnttab.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ static const size_t NR_GRANT_TABLE_ENTRIES =

static grant_entry_v1_t *gnttab_table;

/*
* Grant pages imported from other Xen domains must use a separate virtual
* memory address space. Solo5 maps us an extra 1GB address space at physical
* address 4GB (see bindings/xen/platform.c), of which we use a suitably sized
* area for this purpose.
*/
#define NR_GNTMAP_AREA_PAGES (2 * NR_GRANT_TABLE_ENTRIES)
static bmap_allocator_t *gnttab_alloc;

void gnttab_init(void)
{
int rc = posix_memalign((void **)&gnttab_table, PAGE_SIZE,
Expand All @@ -65,6 +74,15 @@ void gnttab_init(void)
}
DPRINTF(1, "pages = %zd, entries = %zd\n", NR_GRANT_TABLE_PAGES,
NR_GRANT_TABLE_ENTRIES);

uint64_t gntmap_area_addr;
size_t gntmap_area_size;
solo5__xen_get_gntmap_area(&gntmap_area_addr, &gntmap_area_size);
assert(NR_GNTMAP_AREA_PAGES <= (gntmap_area_size / PAGE_SIZE));
gnttab_alloc = bmap_init(gntmap_area_addr, NR_GNTMAP_AREA_PAGES);
assert(gnttab_alloc);
DPRINTF(1, "gntmap area: %zd pages @ 0x%lx\n", NR_GNTMAP_AREA_PAGES,
gntmap_area_addr);
}

/*
Expand Down Expand Up @@ -225,8 +243,8 @@ mirage_xen_gnttab_map(value v_unused_ctx, value v_ref, value v_domid,
void *addr;
int rc;

rc = posix_memalign(&addr, PAGE_SIZE, 1 * PAGE_SIZE);
if (rc != 0)
addr = bmap_alloc(gnttab_alloc, 1);
if (addr == NULL)
caml_raise_out_of_memory();

v_mapping = alloc_mapping(1);
Expand All @@ -238,7 +256,7 @@ mirage_xen_gnttab_map(value v_unused_ctx, value v_ref, value v_domid,
mapping->entries[0].handle = ref;
rc = gnttab_map(mapping);
if (rc != 0) {
free(mapping->start_addr);
bmap_free(gnttab_alloc, mapping->start_addr, mapping->count);
mapping->start_addr = NULL;
mapping->count = 0;
caml_failwith("mirage_xen_gnttab_map: failed");
Expand Down Expand Up @@ -270,8 +288,8 @@ mirage_xen_gnttab_mapv(value v_unused_ctx, value v_array, value v_writable)
void *addr;
int rc;

rc = posix_memalign(&addr, PAGE_SIZE, count * PAGE_SIZE);
if (rc != 0)
addr = bmap_alloc(gnttab_alloc, count);
if (addr == NULL)
caml_raise_out_of_memory();

v_mapping = alloc_mapping(count);
Expand All @@ -287,7 +305,7 @@ mirage_xen_gnttab_mapv(value v_unused_ctx, value v_array, value v_writable)
}
rc = gnttab_map(mapping);
if (rc != 0) {
free(mapping->start_addr);
bmap_free(gnttab_alloc, mapping->start_addr, count);
mapping->start_addr = NULL;
mapping->count = 0;
caml_failwith("mirage_xen_gnttab_mapv: failed");
Expand Down Expand Up @@ -317,7 +335,7 @@ mirage_xen_gnttab_unmap(value v_unused_ctx, value v_mapping)
rc = gnttab_unmap(mapping);
if (rc != 0)
caml_failwith("mirage_xen_gnttab_unmap: failed");
free(mapping->start_addr);
bmap_free(gnttab_alloc, mapping->start_addr, mapping->count);
mapping->start_addr = 0;
mapping->count = 0;
CAMLreturn(Val_unit);
Expand Down
2 changes: 1 addition & 1 deletion lib/dune
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
(archive_name mirage-xen_bindings)
(language c)
(include_dirs ../include)
(names main evtchn gnttab
(names main bmap evtchn gnttab
alloc_pages_stubs atomic_stubs barrier_stubs checksum_stubs clock_stubs
cstruct_stubs mm_stubs)
(flags (:include cflags.sexp) -O2 -std=c99 -Wall -Werror
Expand Down