Commit 40bfcbdb authored by Charlie Jacobsen's avatar Charlie Jacobsen Committed by Vikram Narayanan

Breaks apart microkernel into smaller pieces, and refactors regression tests.

Trying to make the code base saner for others to read through.

Tests are no longer ran every time you insert the microkernel. They are built
as a separate kernel module (in virt/lcd-domains/tests/lcd-tests.ko).

Ran tests again, and some of the LCD functional tests. All appears OK.
parent ef040dc7
......@@ -5,6 +5,7 @@
#include <linux/spinlock.h>
#include <linux/bitmap.h>
#include <lcd-domains/types.h>
#include <linux/mutex.h>
extern int lcd_on_cpu;
extern int lcd_in_non_root;
......
......@@ -793,127 +793,6 @@ static int setup_vmcs_config(struct vmx_vmcs_config *vmcs_conf)
return 0;
}
/* VMX INIT / EXIT -------------------------------------------------- */
static int main_tests(void);
int lcd_arch_init(void)
{
int ret;
int cpu;
/*
* Check For VMX Features
*/
if (!cpu_has_vmx()) {
LCD_ARCH_ERR("CPU does not support VMX\n");
return -EIO;
}
if (setup_vmcs_config(&vmcs_config) < 0)
return -EIO;
/*
* Set up default MSR bitmap
*/
msr_bitmap = (unsigned long *)__get_free_page(GFP_KERNEL);
if (!msr_bitmap) {
ret = -ENOMEM;
goto failed1;
}
memset(msr_bitmap, 0xff, PAGE_SIZE);
vmx_disable_intercept_for_msr(msr_bitmap, MSR_FS_BASE);
vmx_disable_intercept_for_msr(msr_bitmap, MSR_GS_BASE);
/*
* Initialize VPID bitmap spinlock
*/
spin_lock_init(&vpids.lock);
/*
* VPID 0 is reserved for host. See INVVPID instruction.
*/
set_bit(0, vpids.bitmap);
/*
* Allocate vmxon buffers for each cpu. A vmxon buffer is
* (currently) the same size as a vmcs, so we can re-use
* the vmx_alloc_vmcs routine.
*/
for_each_possible_cpu(cpu) {
struct lcd_arch_vmcs *vmxon_buf;
vmxon_buf = vmx_alloc_vmcs(cpu);
if (!vmxon_buf) {
vmx_free_vmxon_areas();
return -ENOMEM;
}
per_cpu(vmxon_area, cpu) = vmxon_buf;
}
/*
* Turn on vmx on each cpu
*
* Note: on_each_cpu disables preemption
*/
atomic_set(&vmx_enable_failed, 0);
if (on_each_cpu(vmx_enable, NULL, 1)) {
LCD_ARCH_ERR("timeout waiting for VMX mode enable.\n");
ret = -EIO;
goto failed1; /* sadly we can't totally recover */
}
if (atomic_read(&vmx_enable_failed)) {
ret = -EBUSY;
goto failed2;
}
/*
* Init lcd_arch_thread cache (using instead of kmalloc since
* these structs need to be aligned properly)
*/
lcd_arch_cache = KMEM_CACHE(lcd_arch, 0);
if (!lcd_arch_cache) {
LCD_ARCH_ERR("failed to set up kmem cache\n");
ret = -ENOMEM;
goto failed3;
}
/*
* Run tests
*/
if (main_tests()) {
ret = -1;
goto failed4;
}
return 0;
failed4:
kmem_cache_destroy(lcd_arch_cache);
failed3:
failed2:
on_each_cpu(vmx_disable, NULL, 1);
failed1:
vmx_free_vmxon_areas();
free_page((unsigned long)msr_bitmap);
return ret;
}
void lcd_arch_exit(void)
{
on_each_cpu(vmx_disable, NULL, 1);
vmx_free_vmxon_areas();
free_page((unsigned long)msr_bitmap);
kmem_cache_destroy(lcd_arch_cache);
}
/* VMX EPT -------------------------------------------------- */
/**
......@@ -4438,6 +4317,115 @@ int lcd_arch_check(struct lcd_arch *t)
return 0;
}
/* VMX INIT / EXIT -------------------------------------------------- */
int lcd_arch_init(void)
{
int ret;
int cpu;
/*
* Check For VMX Features
*/
if (!cpu_has_vmx()) {
LCD_ARCH_ERR("CPU does not support VMX\n");
return -EIO;
}
if (setup_vmcs_config(&vmcs_config) < 0)
return -EIO;
/*
* Set up default MSR bitmap
*/
msr_bitmap = (unsigned long *)__get_free_page(GFP_KERNEL);
if (!msr_bitmap) {
ret = -ENOMEM;
goto failed1;
}
memset(msr_bitmap, 0xff, PAGE_SIZE);
vmx_disable_intercept_for_msr(msr_bitmap, MSR_FS_BASE);
vmx_disable_intercept_for_msr(msr_bitmap, MSR_GS_BASE);
/*
* Initialize VPID bitmap spinlock
*/
spin_lock_init(&vpids.lock);
/*
* VPID 0 is reserved for host. See INVVPID instruction.
*/
set_bit(0, vpids.bitmap);
/*
* Allocate vmxon buffers for each cpu. A vmxon buffer is
* (currently) the same size as a vmcs, so we can re-use
* the vmx_alloc_vmcs routine.
*/
for_each_possible_cpu(cpu) {
struct lcd_arch_vmcs *vmxon_buf;
vmxon_buf = vmx_alloc_vmcs(cpu);
if (!vmxon_buf) {
vmx_free_vmxon_areas();
return -ENOMEM;
}
per_cpu(vmxon_area, cpu) = vmxon_buf;
}
/*
* Turn on vmx on each cpu
*
* Note: on_each_cpu disables preemption
*/
atomic_set(&vmx_enable_failed, 0);
if (on_each_cpu(vmx_enable, NULL, 1)) {
LCD_ARCH_ERR("timeout waiting for VMX mode enable.\n");
ret = -EIO;
goto failed1; /* sadly we can't totally recover */
}
if (atomic_read(&vmx_enable_failed)) {
ret = -EBUSY;
goto failed2;
}
/*
* Init lcd_arch_thread cache (using instead of kmalloc since
* these structs need to be aligned properly)
*/
lcd_arch_cache = KMEM_CACHE(lcd_arch, 0);
if (!lcd_arch_cache) {
LCD_ARCH_ERR("failed to set up kmem cache\n");
ret = -ENOMEM;
goto failed3;
}
return 0;
failed3:
failed2:
on_each_cpu(vmx_disable, NULL, 1);
failed1:
vmx_free_vmxon_areas();
free_page((unsigned long)msr_bitmap);
return ret;
}
void lcd_arch_exit(void)
{
on_each_cpu(vmx_disable, NULL, 1);
vmx_free_vmxon_areas();
free_page((unsigned long)msr_bitmap);
kmem_cache_destroy(lcd_arch_cache);
}
/* EXPORTS -------------------------------------------------- */
EXPORT_SYMBOL(lcd_arch_init);
......@@ -4462,8 +4450,6 @@ EXPORT_SYMBOL(lcd_arch_check);
/* DEBUGGING -------------------------------------------------- */
#include "tests/main-tests.c"
int lcd_on_cpu = -1;
int lcd_in_non_root = 0;
......
......@@ -2,17 +2,9 @@
* Regression tests for lcd arch code.
*/
static int test01(void)
{
struct lcd_arch_vmcs *vmcs;
vmcs = vmx_alloc_vmcs(raw_smp_processor_id());
if (!vmcs) {
LCD_ARCH_ERR("failed");
return -1;
}
vmx_free_vmcs(vmcs);
return 0;
}
#include <asm/lcd-domains/lcd-domains.h>
#include <lcd-domains/tests-util.h>
#include <linux/gfp.h>
static int test02(void)
{
......@@ -204,80 +196,19 @@ fail1:
return ret;
}
static int test05(void)
void arch_tests(void)
{
if (!vmx_addr_is_canonical(0UL)) {
LCD_ARCH_ERR("failed");
return -1;
}
if (vmx_addr_is_canonical(1UL << 63)) {
LCD_ARCH_ERR("failed");
return -1;
}
if (vmx_addr_is_canonical(0xFFFFUL << 48)) {
LCD_ARCH_ERR("failed");
return -1;
}
if (!vmx_addr_is_canonical(0xFFFF8UL << 44)) {
LCD_ARCH_ERR("failed");
return -1;
}
if (!vmx_addr_is_canonical(0x00007UL << 44)) {
LCD_ARCH_ERR("failed");
return -1;
}
int n = 0;
int total = 3;
return 0;
}
RUN_TEST(test02, n);
RUN_TEST(test03, n);
RUN_TEST(test04, n);
static int test06(void)
{
u32 width;
width = cpuid_eax(0x80000008) & 0xff;
if (vmx_bad_phys_addr(0xff)) {
LCD_ARCH_ERR("failed");
return -1;
if (n < total) {
LCD_ARCH_ERR("%d of %d arch tests failed",
(total - n), total);
} else {
LCD_ARCH_MSG("all lcd arch tests passed!");
}
if (vmx_bad_phys_addr(1UL << (width - 1))) {
LCD_ARCH_ERR("failed");
return -1;
}
if (!vmx_bad_phys_addr(1UL << width)) {
LCD_ARCH_ERR("failed");
return -1;
}
if (!vmx_bad_phys_addr(-1ULL)) {
LCD_ARCH_ERR("failed");
return -1;
}
if (width >= 40 && vmx_bad_phys_addr(0x30682f000)) {
LCD_ARCH_ERR("failed");
return -1;
}
return 0;
}
static int main_tests(void)
{
if (test01())
return -1;
if (test02())
return -1;
if (test03())
return -1;
if (test04())
return -1;
if (test05())
return -1;
if (test06())
return -1;
LCD_ARCH_MSG("all lcd arch main tests passed!");
return 0;
}
......@@ -22,6 +22,8 @@ struct lcd_info;
int __klcd_alloc_cptr(struct cptr_cache *cptr_cache, cptr_t *free_cptr);
void __klcd_free_cptr(struct cptr_cache *cptr_cache, cptr_t c);
int klcd_init_cptr(struct cptr_cache **c_out);
void klcd_destroy_cptr(struct cptr_cache *c);
int klcd_alloc_cptr(cptr_t *free_slot);
void klcd_free_cptr(cptr_t c);
int klcd_add_page(struct page *p, cptr_t *slot_out);
......@@ -29,6 +31,8 @@ void klcd_rm_page(cptr_t slot);
int klcd_enter(void);
void klcd_exit(int retval);
int klcd_page_alloc(cptr_t *slot_out, gpa_t gpa);
int klcd_pages_alloc(cptr_t *slots_out, hpa_t *hp_base_out,
hva_t *hv_base_out, unsigned order);
int klcd_gfp(cptr_t *slot_out, gpa_t *gpa_out, gva_t *gva_out);
int klcd_create_sync_endpoint(cptr_t *slot_out);
int klcd_send(cptr_t endpoint);
......@@ -168,6 +172,31 @@ static inline int lcd_page_alloc(cptr_t *slot_out, gpa_t gpa)
{
return klcd_page_alloc(slot_out, gpa);
}
/**
* Allocate 2**order zero'd out pages, and put capabilities in slots_out.
* slots_out should be an array with at least 2**order slots. Returns
* guest physical and virtual addresses of first page. (For KLCDs, the
* guest physical and virtual will be == to the host physical and virtual.)
*/
static inline int lcd_pages_alloc(cptr_t *slots_out, gpa_t *gp_base_out,
gva_t *gv_base_out, unsigned order)
{
hpa_t hp_base_out;
hva_t hv_base_out;
int ret;
ret = klcd_pages_alloc(slots_out, &hp_base_out, &hv_base_out, order);
if (ret)
return ret;
/*
* For KLCDs, gpa = hpa, gva = hva.
*/
*gp_base_out = __gpa(hpa_val(hp_base_out));
*gv_base_out = __gva(hva_val(hv_base_out));
return ret;
}
/**
* Higher level routine to get a free page. Maps it in guest physical
* and virtual address spaces. Returns slot and addresses.
......@@ -326,7 +355,22 @@ static inline int lcd_cap_revoke(cptr_t slot)
/* CPTR CACHE -------------------------------------------------- */
/**
* Initialize the cptr cache.
*
* This should be called when an lcd boots.
*/
static inline int lcd_init_cptr(void)
{
return klcd_init_cptr(&current->cptr_cache);
}
/**
* This should be called before an lcd exits.
*/
static inline void lcd_destroy_cptr(void)
{
klcd_destroy_cptr(current->cptr_cache);
}
/**
* Find an unused cptr (a cptr that refers to an unused cnode).
*/
......@@ -341,11 +385,18 @@ static inline void lcd_free_cptr(cptr_t c)
{
return klcd_free_cptr(c);
}
/**
* This is needed when an lcd is creating another lcd (it needs to set up
* the other lcd's cptr cache).
*/
static inline int __lcd_alloc_cptr(struct cptr_cache *cache,
cptr_t *free_slot)
{
return __klcd_alloc_cptr(cache, free_slot);
}
/**
* Same comment as __lcd_alloc_cptr.
*/
static inline void __lcd_free_cptr(struct cptr_cache *cache, cptr_t c)
{
return __klcd_free_cptr(cache, c);
......@@ -414,7 +465,7 @@ struct lcd_info {
/*
* The creating lcd has a cptr to the boot page
*/
cptr_t *boot_page_cptrs;
cptr_t boot_page_cptrs[1 << LCD_BOOT_PAGES_ORDER];
/*
* Boot mem page infos
*/
......
/**
* util.h - common test helper stuff.
*/
#ifndef LCD_DOMAINS_TESTS_UTIL_H
#define LCD_DOMAINS_TESTS_UTIL_H
#include <linux/kernel.h>
#define RUN_TEST(func, counter) { \
if (func()) { \
printk("test %s failed\n", #func); \
} else { \
counter++; \
} \
}
#endif /* LCD_DOMAINS_TESTS_UTIL_H */
......@@ -14,6 +14,12 @@ config LCD_DOMAINS
config HAVE_LCD
bool
config LCD_DOMAINS_TESTS
tristate "Tests of Light-weight Capability Domains"
depends on LCD_DOMAINS
default m
---help---
Simple unit tests for lcd domains. Loading the module runs the tests.
menuconfig LCD_TEST_MOD
bool "Test Modules"
......
......@@ -7,13 +7,19 @@ ccflags-y += -Werror
# The module
obj-$(CONFIG_LCD_DOMAINS) += lcd-domains.o
lcd-domains-y := main.o ipc.o kliblcd.o cap.o
# microkernel
lcd-domains-y += microkernel/main.o microkernel/ipc.o \
microkernel/cap.o
# arch-dependent code
lcd-domains-y += ../../arch/$(ARCH)/lcd-domains/main.o
# Test modules
# kliblcd
lcd-domains-y += kliblcd/init.o kliblcd/mem.o \
kliblcd/ipc.o kliblcd/enterexit.o \
kliblcd/create.o kliblcd/cap.o \
kliblcd/cptr_cache.o
# Unit tests module
obj-$(CONFIG_LCD_DOMAINS_TESTS) += tests/
# Test modules
obj-$(CONFIG_LCD_TEST_MOD) += test-mods/
# LCD Debug
obj-$(CONFIG_LCD_DEBUG) += debug/
......@@ -350,6 +350,9 @@ int __klcd_enter(void);
void __klcd_exit(void);
int __klcd_page_zalloc(struct lcd *klcd, cptr_t c, hpa_t *hpa_out,
hva_t *hva_out);
int __klcd_pages_zalloc(struct lcd *klcd, cptr_t *slots,
hpa_t *hp_base_out, hva_t *hv_base_out,
unsigned order);
int __lcd_create(struct lcd *caller, cptr_t slot, gpa_t stack);
int __lcd_create__(struct lcd **out);
int __lcd_config(struct lcd *caller, cptr_t lcd, gva_t pc, gva_t sp,
......
/**
* cap.c
*
* Authors:
* Charlie Jacobsen <charlesj@cs.utah.edu>
*/
#include <lcd-domains/kliblcd.h>
#include <lcd-domains/utcb.h>
#include <lcd-domains/types.h>
#include "../internal.h"
int klcd_cap_grant(cptr_t lcd, cptr_t src, cptr_t dest)
{
return __lcd_cap_grant_cheat(current->lcd, lcd, src, dest);
}
int klcd_cap_page_grant_map(cptr_t lcd, cptr_t page, cptr_t dest, gpa_t gpa)
{
return __lcd_cap_page_grant_map_cheat(current->lcd, lcd, page, dest,
gpa);
}
void klcd_cap_delete(cptr_t slot)
{
/*
* Delete capability from cspace
*/
__lcd_cap_delete(&current->lcd->cspace, slot);
/*
* Return cptr
*/
lcd_free_cptr(slot);
}
int klcd_cap_revoke(cptr_t slot)
{
/*
* Revoke child capabilities
*
* XXX: How do the lcd's know these slots are now free? The microkernel
* won't tell them.
*/
return __lcd_cap_revoke(&current->lcd->cspace, slot);
}
/* EXPORTS -------------------------------------------------- */
EXPORT_SYMBOL(klcd_cap_grant);
EXPORT_SYMBOL(klcd_cap_page_grant_map);
EXPORT_SYMBOL(klcd_cap_delete);
EXPORT_SYMBOL(klcd_cap_revoke);
/**
* cptr_cache.c
*
* Authors:
* Charlie Jacobsen <charlesj@cs.utah.edu>
*/
#include <linux/slab.h>
#include <lcd-domains/kliblcd.h>
#include <lcd-domains/utcb.h>
#include <lcd-domains/types.h>
#include <linux/mutex.h>
#include "../internal.h"
static int cptr_cache_init(struct cptr_cache **out)
{
struct cptr_cache *cache;
int ret;
int i, j;
int nbits;
/*
* Allocate the container
*/
cache = kzalloc(sizeof(*cache), GFP_KERNEL);
if (!cache) {
ret = -ENOMEM;
goto fail1;
}
/*
* Allocate the bitmaps
*/
for (i = 0; i < (1 << LCD_CPTR_DEPTH_BITS); i++) {
/*
* For level i, we use the slot bits plus i * fanout bits
*
* So e.g. for level 0, we use only slot bits, so there
* are only 2^(num slot bits) cap slots at level 0.
*/
nbits = 1 << (LCD_CPTR_SLOT_BITS + i * LCD_CPTR_FANOUT_BITS);
/*
* Alloc bitmap
*/
cache->bmaps[i] = kzalloc(sizeof(unsigned long) *
BITS_TO_LONGS(nbits),
GFP_KERNEL);
if (!cache->bmaps[i]) {
ret = -ENOMEM;
goto fail2; /* i = level we failed at */
}
}
/*
* Mark reserved cptr's as allocated
*/
set_bit(0, cache->bmaps[0]);
set_bit(1, cache->bmaps[0]);
set_bit(2, cache->bmaps[0]);
*out = cache;
return 0;
fail2:
for (j = 0; j < i; j++)
kfree(cache->bmaps[j]);
kfree(cache);
fail1:
return ret;
}
static void cptr_cache_destroy(struct cptr_cache *cache)
{
int i;
/*
* Free bitmaps
*/
for (i = 0; i < (1 << LCD_CPTR_DEPTH_BITS); i++)
kfree(cache->bmaps[i]);
/*
* Free container
*/
kfree(cache);
}
static int __lcd_alloc_cptr_from_bmap(unsigned long *bmap, int size,
unsigned long *out)
{
unsigned long idx;
/*
* Find next zero bit
*/
idx = find_first_zero_bit(bmap, size);
if (idx >= size)
return 0; /* signal we are full */
/*
* Set bit to mark cptr as in use
*/
set_bit(idx, bmap);
*out = idx;
return 1; /* signal we are done */
}
int __klcd_alloc_cptr(struct cptr_cache *cptr_cache, cptr_t *free_cptr)
{
int ret;
int depth;
int done;
unsigned long *bmap;
unsigned long idx;
int size;
depth = 0;
do {
bmap = cptr_cache->bmaps[depth];
size = 1 << (LCD_CPTR_SLOT_BITS +
depth * LCD_CPTR_FANOUT_BITS);
done = __lcd_alloc_cptr_from_bmap(bmap, size, &idx);
depth++;
} while (!done && depth < (1 << LCD_CPTR_DEPTH_BITS));
if (!done) {
/*
* Didn't find one
*/
LCD_ERR("out of cptrs");
ret = -ENOMEM;
goto fail2;
}
/*
* Found one; dec depth back to what it was, and encode
* depth in cptr
*/
depth--;
idx |= (depth << LCD_CPTR_LEVEL_SHIFT);
*free_cptr = __cptr(idx);
return 0;