blob: 13a96809ac658aefd1a81512d935b9b867a668aa [file] [log] [blame] [edit]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <sel4test-driver/gen_config.h>
#include <assert.h>
#include <stdio.h>
#include <sel4/sel4.h>
#include <vka/object.h>
#include "../helpers.h"
static volatile int revoking = 0;
static volatile int preempt_count = 0;
static int revoke_func(seL4_CNode service, seL4_Word index, seL4_Word depth)
{
revoking = 1;
seL4_CNode_Revoke(service, index, depth);
revoking = 2;
return 0;
}
static int preempt_count_func(env_t env)
{
while (revoking < 2) {
sel4test_ntfn_timer_wait(env);
if (revoking == 1) {
preempt_count++;
}
}
return 0;
}
static int create_cnode_table(env_t env, int num_cnode_bits, seL4_CPtr ep)
{
/* Create as many cnodes as possible. We will copy the cap into all
* those cnodes. */
#define CNODE_SIZE_BITS 12
int num_caps = 0;
int error = 0;
ZF_LOGD(" Creating %d caps .", BIT((num_cnode_bits + CNODE_SIZE_BITS)));
for (int i = 0; i < (1 << num_cnode_bits); i++) {
seL4_CPtr ctable = vka_alloc_cnode_object_leaky(&env->vka, CNODE_SIZE_BITS);
if (ctable == seL4_CapNull) {
return -1;
}
for (int j = 0; j < BIT(CNODE_SIZE_BITS); j++) {
error = seL4_CNode_Copy(
ctable, j, CNODE_SIZE_BITS,
env->cspace_root, ep, seL4_WordBits,
seL4_AllRights);
test_check(!error);
num_caps++;
}
ZF_LOGD(".");
}
if (num_caps != BIT(num_cnode_bits + CNODE_SIZE_BITS)) {
ZF_LOGD("Created %d caps. Couldn't create the required number of caps %d",
num_caps,
BIT(num_cnode_bits + CNODE_SIZE_BITS));
return -1;
}
return error;
}
static int test_preempt_revoke_actual(env_t env, int num_cnode_bits)
{
helper_thread_t revoke_thread, preempt_thread;
int error;
uint64_t timeout_val = 0;
/* Create an endpoint cap that will be derived many times. */
seL4_CPtr ep = vka_alloc_endpoint_leaky(&env->vka);
create_helper_thread(env, &revoke_thread);
create_helper_thread(env, &preempt_thread);
set_helper_priority(env, &preempt_thread, 101);
set_helper_priority(env, &revoke_thread, 100);
/* First create a ctable and fill it with copied ep caps
* and time the revoke operation
*/
error = create_cnode_table(env, num_cnode_bits, ep);
test_error_eq(error, seL4_NoError);
uint64_t start, end, diff;
/* meaure revoke_func time.
* Note that we don't take caching and other effects (e.g. function calls vs.
* jmp costs into consideration, however, this should get us a better
* timeout value per target compared to setting a fixed timeout value that may
* fail on different targets.
*/
start = sel4test_timestamp(env);
revoke_func(env->cspace_root, ep, seL4_WordBits);
end = sel4test_timestamp(env);
test_geq(end, start);
diff = end - start;
/* Set a timeout value to a third of the revoke operation time,
* to allow preemption to occur at least twice, as the test expects.
*/
timeout_val = diff / 3;
/* Now, actually create a ctable that is gonna be used in the revoke
* test.
*/
error = create_cnode_table(env, num_cnode_bits, ep);
test_error_eq(error, seL4_NoError);
sel4test_periodic_start(env, timeout_val);
/* Last thread to start runs first. */
revoking = 0;
preempt_count = 0;
start_helper(env, &preempt_thread, (helper_fn_t) preempt_count_func, (seL4_Word) env, 0, 0, 0);
start_helper(env, &revoke_thread, (helper_fn_t) revoke_func, env->cspace_root,
ep, seL4_WordBits, 0);
wait_for_helper(&revoke_thread);
cleanup_helper(env, &preempt_thread);
cleanup_helper(env, &revoke_thread);
ZF_LOGD(" %d preemptions\n", preempt_count);
return preempt_count;
}
static int test_preempt_revoke(env_t env)
{
for (int num_cnode_bits = 1; num_cnode_bits < 32; num_cnode_bits++) {
int result = test_preempt_revoke_actual(env, num_cnode_bits);
if (result > 1) {
return sel4test_get_result();
} else if (result == -1) {
/* At this point we assume this error is a problem where the time of
* revoc_func < timer_resolution, which will cause set timeout to faule
* return an error. Skip such small cnodes and only work with cnode sizes where
* revoc_func > timer_resolution
*/
continue;
}
}
ZF_LOGD("Couldn't trigger preemption point with millions of caps!\n");
test_assert(0);
}
DEFINE_TEST(PREEMPT_REVOKE, "Test preemption path in revoke", test_preempt_revoke, config_set(CONFIG_HAVE_TIMER))