1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <stdio.h>
14#include <sel4/sel4.h>
15#include <vka/object.h>
16
17#include "../test.h"
18#include "../helpers.h"
19
20#define NUM_BADGED_CLIENTS 32
21
22static int bouncer_func(seL4_CPtr ep, seL4_CPtr reply, seL4_Word arg2, seL4_Word arg3)
23{
24    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 0);
25    seL4_Word sender_badge;
26    api_recv(ep, &sender_badge, reply);
27    while (1) {
28        api_reply_recv(ep, tag, &sender_badge, reply);
29    }
30    return 0;
31}
32
33static int call_func(seL4_CPtr ep, seL4_Word msg, volatile seL4_Word *done, seL4_Word arg3)
34{
35    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 1);
36
37    /* Send the given message once. */
38    seL4_SetMR(0, msg);
39    tag = seL4_Call(ep, tag);
40    test_check(seL4_MessageInfo_get_length(tag) == 1);
41    test_check(seL4_GetMR(0) == ~msg);
42
43    *done = 0;
44
45    /* Send the given message again - should (eventually) fault this time. */
46    seL4_SetMR(0, msg);
47    tag = seL4_Call(ep, tag);
48    /* The call should fail. */
49    test_check(seL4_MessageInfo_get_label(tag) == seL4_InvalidCapability);
50
51    *done = 1;
52
53    return sel4test_get_result();
54}
55
56static int test_ep_cancelBadgedSends(env_t env)
57{
58    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 0);
59    struct {
60        helper_thread_t thread;
61        seL4_CPtr badged_ep;
62        seL4_CPtr derived_badged_ep;
63        volatile seL4_Word done;
64    } senders[NUM_BADGED_CLIENTS];
65    helper_thread_t bouncer;
66    seL4_CPtr bounce_ep;
67    UNUSED int error;
68    seL4_CPtr ep;
69
70    /* Create the master endpoint. */
71    ep = vka_alloc_endpoint_leaky(&env->vka);
72
73    /* Create N badged endpoints, and derive each of them. */
74    for (int i = 0; i < NUM_BADGED_CLIENTS; i++) {
75        senders[i].badged_ep = get_free_slot(env);
76        assert(senders[i].badged_ep != 0);
77
78        senders[i].derived_badged_ep = get_free_slot(env);
79        assert(senders[i].derived_badged_ep != 0);
80
81        error = cnode_mint(env, ep, senders[i].badged_ep, seL4_AllRights, i + 200);
82        assert(!error);
83
84        error = cnode_copy(env, senders[i].badged_ep, senders[i].derived_badged_ep, seL4_AllRights);
85        assert(!error);
86
87        create_helper_thread(env, &senders[i].thread);
88        set_helper_priority(env, &senders[i].thread, 100);
89
90        senders[i].done = -1;
91    }
92    /* Create a bounce thread so we can get lower prio threads to run. */
93    bounce_ep = vka_alloc_endpoint_leaky(&env->vka);
94    create_helper_thread(env, &bouncer);
95    set_helper_priority(env, &bouncer, 0);
96    start_helper(env, &bouncer, bouncer_func, bounce_ep, get_helper_reply(&bouncer), 0, 0);
97
98    for (int i = 0; i < NUM_BADGED_CLIENTS; i++) {
99        start_helper(env, &senders[i].thread, (helper_fn_t) call_func,
100                     senders[i].derived_badged_ep, i + 100, (seL4_Word) &senders[i].done, 0);
101    }
102
103    /* Let the sender threads run. */
104    seL4_CPtr reply = vka_alloc_reply_leaky(&env->vka);
105
106    seL4_Call(bounce_ep, tag);
107    /* Receive a message from each endpoint and check the badge. */
108    for (int i = 0; i < NUM_BADGED_CLIENTS; i++) {
109        seL4_Word sender_badge;
110        seL4_MessageInfo_ptr_set_length(&tag, 1);
111        tag = api_recv(ep, &sender_badge, reply);
112        assert(seL4_MessageInfo_get_length(tag) == 1);
113        assert(seL4_GetMR(0) == sender_badge - 100);
114        seL4_SetMR(0, ~seL4_GetMR(0));
115        api_reply(reply, tag);
116    }
117    /* Let the sender threads run. */
118    seL4_Call(bounce_ep, tag);
119    /* Check none of the threads have failed yet. */
120    for (int i = 0; i < NUM_BADGED_CLIENTS; i++) {
121        assert(senders[i].done == 0);
122    }
123    /* cancelBadgedSends each endpoint. */
124    for (int i = 0; i < NUM_BADGED_CLIENTS; i++) {
125        error = cnode_revoke(env, senders[i].badged_ep);
126        test_eq(error, seL4_NoError);
127
128        error = cnode_cancelBadgedSends(env, senders[i].badged_ep);
129        assert(!error);
130
131        /* Let thread run. */
132        seL4_Call(bounce_ep, tag);
133        /* Check that only the intended threads have now aborted. */
134        for (int j = 0; j < NUM_BADGED_CLIENTS; j++) {
135            if (j <= i) {
136                assert(senders[j].done == 1);
137            } else {
138                assert(senders[j].done == 0);
139            }
140        }
141    }
142    seL4_Call(bounce_ep, tag);
143    for (int i = 0; i < NUM_BADGED_CLIENTS; i++) {
144        cleanup_helper(env, &senders[i].thread);
145    }
146    cleanup_helper(env, &bouncer);
147
148    return sel4test_get_result();
149}
150DEFINE_TEST(CANCEL_BADGED_SENDS_0001, "Basic endpoint cancelBadgedSends testing.", test_ep_cancelBadgedSends, true)
151
152static int ep_test_func(seL4_CPtr sync_ep, seL4_CPtr test_ep, volatile seL4_Word *status, seL4_CPtr reply)
153{
154    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 0);
155    seL4_Word sender_badge;
156    while (1) {
157        api_recv(sync_ep, &sender_badge, reply);
158        /* Hit up the test end point */
159        seL4_MessageInfo_t reply_tag = seL4_Call(test_ep, tag);
160        /* See what the status was */
161        *status = !!(seL4_MessageInfo_get_label(reply_tag) != seL4_InvalidCapability);
162        /* Reply */
163        api_reply(reply, tag);
164    }
165    return sel4test_get_result();
166}
167
168/* CANCEL_BADGED_SENDS_0001 only tests if a thread gets its IPC canceled. The IPC
169 * can succeed even if the cap it used got deleted provided the final
170 * capability was not cancelBadgedSendsd (thus causing an IPC cancel to happen)
171 * This means that CANCEL_BADGED_SENDS_0001 will pass even if all the derived badges
172 * are deleted, since deleting them did NOT delete the final capability
173 * or cause a cancelBadgedSends so outstanding IPCs were not canceled */
174static int test_ep_cancelBadgedSends2(env_t env)
175{
176    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 0);
177    struct {
178        helper_thread_t thread;
179        seL4_CPtr sync_ep;
180        seL4_CPtr badged_ep;
181        seL4_CPtr derived_badged_ep;
182        volatile seL4_Word last_status;
183    } helpers[NUM_BADGED_CLIENTS];
184    seL4_CPtr ep;
185    helper_thread_t reply_thread;
186    UNUSED int error;
187
188    /* Create the main EP we are testing */
189    ep = vka_alloc_endpoint_leaky(&env->vka);
190    /* spawn a thread to keep replying to any messages on main ep */
191    create_helper_thread(env, &reply_thread);
192    start_helper(env, &reply_thread, bouncer_func, ep, get_helper_reply(&reply_thread), 0, 0);
193    /* Spawn helper threads each with their own sync ep and a badged copy of the main ep */
194    for (int i = 0; i < NUM_BADGED_CLIENTS; i++) {
195        helpers[i].badged_ep = get_free_slot(env);
196        assert(helpers[i].badged_ep != 0);
197
198        helpers[i].derived_badged_ep = get_free_slot(env);
199        assert(helpers[i].derived_badged_ep != 0);
200
201        helpers[i].sync_ep = vka_alloc_endpoint_leaky(&env->vka);
202
203        error = cnode_mint(env, ep, helpers[i].badged_ep, seL4_AllRights, i + 200);
204        assert(!error);
205
206        error = cnode_copy(env, helpers[i].badged_ep, helpers[i].derived_badged_ep, seL4_AllRights);
207        assert(!error);
208
209        helpers[i].last_status = -1;
210
211        create_helper_thread(env, &helpers[i].thread);
212        start_helper(env, &helpers[i].thread, (helper_fn_t) ep_test_func,
213                     helpers[i].sync_ep, helpers[i].derived_badged_ep, (seL4_Word) &helpers[i].last_status,
214                     get_helper_reply(&helpers[i].thread));
215    }
216    /* Test that every thread and endpoint is working */
217    for (int i = 0; i < NUM_BADGED_CLIENTS; i++) {
218        seL4_Call(helpers[i].sync_ep, tag);
219    }
220    for (int i = 0; i < NUM_BADGED_CLIENTS; i++) {
221        test_assert(helpers[i].last_status);
222    }
223    /* Now start recycling endpoints and make sure the correct endpoints disappear */
224    for (int i = 0 ; i < NUM_BADGED_CLIENTS; i++) {
225        error = cnode_revoke(env, helpers[i].badged_ep);
226        test_eq(error, seL4_NoError);
227        /* cancelBadgedSends an ep */
228        error = cnode_cancelBadgedSends(env, helpers[i].badged_ep);
229        assert(!error);
230        /* Now run every thread */
231        for (int j = 0; j < NUM_BADGED_CLIENTS; j++) {
232            seL4_Call(helpers[j].sync_ep, tag);
233        }
234        for (int j = 0; j < NUM_BADGED_CLIENTS; j++) {
235            if (j <= i) {
236                test_assert(!helpers[j].last_status);
237            } else {
238                test_assert(helpers[j].last_status);
239            }
240        }
241    }
242    return sel4test_get_result();
243}
244DEFINE_TEST(CANCEL_BADGED_SENDS_0002, "cancelBadgedSends deletes caps", test_ep_cancelBadgedSends2, true)
245