| /* |
| * 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)) |