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#include <string.h> 13#include <stdio.h> 14 15#include <sel4/sel4.h> 16#include <vka/capops.h> 17#include <allocman/allocman.h> 18#include <allocman/vka.h> 19#include <allocman/bootstrap.h> 20#include <sel4utils/thread.h> 21#include <serial_server/parent.h> 22#include <serial_server/client.h> 23 24#include "../test.h" 25#include "../helpers.h" 26 27#define SERSERV_TEST_PRIO_SERVER (seL4_MaxPrio - 1) 28#define SERSERV_TEST_PRIO_CLIENT (seL4_MinPrio) 29 30#define SERSERV_TEST_N_CLIENTS (1) 31 32#define SERSERV_TEST_ALLOCMAN_PREALLOCATED_MEMSIZE (64 * 1024) 33#define SERSERV_TEST_UT_SIZE (512 * 1024) 34 35static const char *test_str = "Hello, world!\n"; 36 37 38/* These next few tests test the same things from client threads. 39 * 40 * We spawn a bunch of helper threads and then ask those helper threads to 41 * run these same tests, and then have the helpers return the values they 42 * got when they called the Server. 43 * 44 * We keep it simple as well by using some global state to pass data to the 45 * client threads so they can do their work. Then, so that each client knows 46 * which one it is, we pass the clients a numeric ID invented by the test 47 * to identify that client. 48 */ 49typedef struct _client_test_data { 50 helper_thread_t thread; 51 /* "badged_server_ep_cspath2" is used in the disconnect/reconnect tests: 52 * the client can't reconnect using the same badged ep as the first 53 * connection. 54 * 55 * We could make each client seL4_Call() the main thread and ask 56 * it to re-mint the ep-cap to each client, but that imposes extra 57 * complexity for a simple test. So for that particular test we just mint 2 58 * ep caps from the start. 59 */ 60 volatile void **parent_used_pages_list, *client_used_pages_list; 61 int n_used_pages; 62 cspacepath_t badged_server_ep_cspath, badged_server_ep_cspath2, ut_512k_cspath; 63 serial_client_context_t conn; 64} client_test_data_t; 65 66typedef int (client_test_fn)(client_test_data_t *, vka_t *, vspace_t *); 67 68struct { 69 vka_t *vka; 70 vspace_t *vspace; 71 simple_t *simple; 72 client_test_data_t client_test_data[SERSERV_TEST_N_CLIENTS]; 73 uint8_t allocman_mem[SERSERV_TEST_N_CLIENTS][SERSERV_TEST_ALLOCMAN_PREALLOCATED_MEMSIZE]; 74} static client_globals; 75 76/* These next few functions (client_*_main())are the actual tests that are run 77 * by the client threads and processes for the concurrency tests. 78 */ 79 80/** This function will guide a client thread to connect to the server and return the 81 * result. 82 */ 83static int client_connect_main(client_test_data_t *self, vka_t *vka, vspace_t *vspace) 84{ 85 int error = 0; 86 serial_client_context_t conn; 87 88 error = serial_server_client_connect(self->badged_server_ep_cspath.capPtr, 89 vka, vspace, 90 &conn); 91 return error; 92} 93 94/** This function will guide a client thread to connect to the server, send a 95 * printf() request, and return the result. 96 */ 97static int client_printf_main(client_test_data_t *self, vka_t *vka, vspace_t *vspace) 98{ 99 int error; 100 serial_client_context_t conn; 101 102 error = serial_server_client_connect(self->badged_server_ep_cspath.capPtr, 103 vka, vspace, 104 &conn); 105 if (error != 0) { 106 return error; 107 } 108 109 error = serial_server_printf(&conn, test_str); 110 if (error != strlen(test_str)) { 111 return -1; 112 } 113 return 0; 114} 115 116/** This function will guide a client thread to connect to the server, send a 117 * write() request, and return the result. 118 */ 119static int client_write_main(client_test_data_t *self, vka_t *vka, vspace_t *vspace) 120{ 121 int error; 122 serial_client_context_t conn; 123 124 error = serial_server_client_connect(self->badged_server_ep_cspath.capPtr, 125 vka, vspace, 126 &conn); 127 if (error != 0) { 128 return error; 129 } 130 131 error = serial_server_write(&conn, test_str, strlen(test_str)); 132 if (error != strlen(test_str)) { 133 return -1; 134 } 135 return 0; 136} 137 138/** This function will guide a client thread to connect to the server, send a 139 * printf() request, then a write() request, then disconnect(), and then 140 * reconnect to the Server and send another printf() and another write(), 141 * and return the result. 142 */ 143static int client_disconnect_reconnect_printf_write_main(client_test_data_t *self, vka_t *vka, vspace_t *vspace) 144{ 145 int error; 146 serial_client_context_t conn; 147 148 error = serial_server_client_connect(self->badged_server_ep_cspath.capPtr, 149 vka, vspace, 150 &conn); 151 if (error != 0) { 152 return error; 153 } 154 error = serial_server_printf(&conn, test_str); 155 if (error != strlen(test_str)) { 156 return -1; 157 } 158 error = serial_server_write(&conn, test_str, strlen(test_str)); 159 if (error != strlen(test_str)) { 160 return error; 161 } 162 163 serial_server_disconnect(&conn); 164 165 error = serial_server_client_connect(self->badged_server_ep_cspath2.capPtr, 166 vka, vspace, 167 &conn); 168 if (error != 0) { 169 return error; 170 } 171 error = serial_server_printf(&conn, test_str); 172 if (error != strlen(test_str)) { 173 return -1; 174 } 175 error = serial_server_write(&conn, test_str, strlen(test_str)); 176 if (error != strlen(test_str)) { 177 return -1; 178 } 179 return 0; 180} 181 182/** This function will guide a client thread to connect to the server, and then 183 * send a kill() request. 184 */ 185static int client_disconnect_main(client_test_data_t *self, vka_t *vka, vspace_t *vspace) 186{ 187 int error; 188 serial_client_context_t conn; 189 190 error = serial_server_client_connect(self->badged_server_ep_cspath.capPtr, 191 vka, vspace, 192 &conn); 193 if (error != 0) { 194 return error; 195 } 196 197 serial_server_disconnect(&conn); 198 return 0; 199} 200 201static void init_file_globals(struct env *env) 202{ 203 memset(client_globals.client_test_data, 0, 204 sizeof(client_globals.client_test_data)); 205 206 memset(client_globals.allocman_mem, 0, sizeof(client_globals.allocman_mem)); 207 208 client_globals.vka = &env->vka; 209 client_globals.vspace = &env->vspace; 210 client_globals.simple = &env->simple; 211} 212 213static int mint_server_ep_to_clients(struct env *env, bool mint_2nd_ep) 214{ 215 int error; 216 217 for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { 218 /* Ask the serserv library to mint the Server's EP to all the new clients. */ 219 client_test_data_t *curr_client = &client_globals.client_test_data[i]; 220 221 if (curr_client->thread.is_process) { 222 curr_client->badged_server_ep_cspath.capPtr = serial_server_parent_mint_endpoint_to_process( 223 &curr_client->thread.process); 224 if (curr_client->badged_server_ep_cspath.capPtr == 0) { 225 return -1; 226 } 227 } else { 228 error = serial_server_parent_vka_mint_endpoint(&env->vka, 229 &curr_client->badged_server_ep_cspath); 230 if (error != 0) { 231 return -1; 232 } 233 } 234 235 if (mint_2nd_ep) { 236 if (curr_client->thread.is_process) { 237 curr_client->badged_server_ep_cspath2.capPtr = serial_server_parent_mint_endpoint_to_process( 238 &curr_client->thread.process); 239 if (curr_client->badged_server_ep_cspath2.capPtr == 0) { 240 return -1; 241 } 242 } else { 243 error = serial_server_parent_vka_mint_endpoint(&env->vka, 244 &curr_client->badged_server_ep_cspath2); 245 if (error != 0) { 246 return -1; 247 } 248 } 249 } 250 } 251 return 0; 252} 253 254static void create_clients(struct env *env, bool is_process) 255{ 256 for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { 257 client_test_data_t *curr_client = &client_globals.client_test_data[i]; 258 259 if (is_process) { 260 create_helper_process(env, &curr_client->thread); 261 } else { 262 create_helper_thread(env, &curr_client->thread); 263 } 264 } 265} 266 267/** Guides a client "process" to set up a vspace_t instance for its new VSpace. 268 * 269 * This basically entails setting up an allocman_t instance, followed by a 270 * vka_t instance and finally a vspace_t instance. The function requires an 271 * untyped of a "reasonable" size (you determine this based on what this new 272 * process will be doing), a list of used pages in the new process' VSpace, 273 * some allocman initial mem and your pointers. 274 * 275 * @param ut_cap CPtr to an untyped of "reasonable" size that was copied to the 276 * new process before this function was invoked. 277 * @param ut_size_bits The size in bits of the untyped "ut_cap". 278 * @param used_pages_list A NULL-terminated list of virtual pages that are 279 * already occupied within this new process' VSpace. 280 * @param allocman_mem Initial memory set aside for bootstrapping an allocman_t. 281 * @param allocman_mem_size Size of the initial allocman_t memory. 282 * @param allocman[out] Pointer to a pointer to an uninitialized allocman_t 283 * instance. Will be returned with an initialized 284 * allocman_t if this function completes successfully. 285 * @param vka[out] Pointer to an uninitialized vka_t instance. 286 * @param vspace[out] Pointer to an uninitialized vspace_t instance. 287 * @param alloc_data[out] Pointer to an uninitialized alloc data structure. 288 * @return 0 on success; non-zero on error. 289 */ 290static int setup_client_process_allocman_vka_and_vspace(seL4_CPtr ut_cap, size_t ut_size_bits, 291 seL4_CPtr first_free_cptr, 292 volatile void **used_pages_list, 293 uint8_t *allocman_mem, size_t allocman_mem_size, 294 allocman_t **allocman, 295 vka_t *vka, vspace_t *vspace, 296 sel4utils_alloc_data_t *alloc_data) 297{ 298 cspacepath_t ut_cspath; 299 uintptr_t paddr; 300 int error; 301 302 if (used_pages_list == NULL || allocman_mem == NULL 303 || allocman == NULL || vspace == NULL || vka == NULL 304 || alloc_data == NULL || allocman_mem_size == 0) { 305 return seL4_InvalidArgument; 306 } 307 *allocman = bootstrap_use_current_1level(SEL4UTILS_CNODE_SLOT, 308 TEST_PROCESS_CSPACE_SIZE_BITS, 309 first_free_cptr, 310 BIT(TEST_PROCESS_CSPACE_SIZE_BITS), 311 allocman_mem_size, 312 allocman_mem); 313 if (*allocman == NULL) { 314 return -1; 315 } 316 317 allocman_make_vka(vka, *allocman); 318 vka_cspace_make_path(vka, ut_cap, &ut_cspath); 319 error = allocman_utspace_add_uts(*allocman, 1, &ut_cspath, 320 &ut_size_bits, 321 &paddr, ALLOCMAN_UT_KERNEL); 322 if (error != 0) { 323 return error; 324 } 325 326 /* Pass the shmem page containing the list of already allocated frames, to 327 * the vspace bootstrap. 328 */ 329 return sel4utils_bootstrap_vspace_leaky(vspace, alloc_data, 330 SEL4UTILS_PD_SLOT, vka, 331 (void **)used_pages_list); 332} 333 334/** Common entry point for all client thread and processes alike. 335 * 336 * This is what is passed to start_helper so that it can do its init work. 337 * This function invokes the actual test routine after it finishes working. 338 * 339 * Determines whether the new client is a thread or process. If it's a process 340 * we do some extra processing, vis-a-vis setting up the new process' own 341 * vka, vspace and allocman, so it can call the library. 342 * 343 * @param _test_fn The actual test function that is to be invoked. 344 * @param _used_pages_list List of used virtual pages in the VSpace of the new 345 * client. This is used to initialize the new client's 346 * vspace_t instance. 347 * @param n_used_pages Number of elements in the _used_pages_list. 348 * @param thread_num The "ID" of this thread as the parent assigned it. 349 * @return 0 on success; non-zero on error. 350 */ 351static int client_common_entry_point(seL4_Word _test_fn, seL4_Word _used_pages_list, 352 seL4_Word n_used_pages, seL4_Word thread_num) 353{ 354 bool is_process; 355 client_test_data_t *client_data; 356 volatile void **used_pages_list = (volatile void **)_used_pages_list; 357 client_test_fn *test_fn = (client_test_fn *)_test_fn; 358 int error; 359 vka_t *vka, _vka; 360 vspace_t *vspace, _vspace; 361 sel4utils_alloc_data_t alloc_data; 362 allocman_t *allocman; 363 364 is_process = used_pages_list != NULL; 365 366 if (is_process) { 367 /* +1 because the list is NULL terminated and we want to get past the 368 * end of the list. 369 */ 370 client_data = (client_test_data_t *)&used_pages_list[n_used_pages + 1]; 371 vka = &_vka; 372 vspace = &_vspace; 373 error = setup_client_process_allocman_vka_and_vspace(client_data->ut_512k_cspath.capPtr, 374 BYTES_TO_SIZE_BITS(SERSERV_TEST_UT_SIZE), 375 client_data->ut_512k_cspath.capPtr + 0x8, 376 used_pages_list, 377 client_globals.allocman_mem[thread_num], 378 SERSERV_TEST_ALLOCMAN_PREALLOCATED_MEMSIZE, 379 &allocman, 380 vka, vspace, 381 &alloc_data); 382 test_eq(error, 0); 383 } else { 384 client_data = &client_globals.client_test_data[thread_num]; 385 vka = client_globals.vka; 386 vspace = client_globals.vspace; 387 } 388 return test_fn(client_data, vka, vspace); 389} 390 391static void start_clients(struct env *env, client_test_fn *test_fn) 392{ 393 bool is_process = client_globals.client_test_data[0].thread.is_process; 394 395 for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { 396 client_test_data_t *curr_client = &client_globals.client_test_data[i]; 397 /* The client threads don't need a list of used pages. The client 398 * processes do need it -- so if the used pages list is NULL, the newly 399 * spawned client assumes it's a thread and not a process. 400 */ 401 volatile void **used_pages_list = ((is_process) 402 ? (volatile void **)curr_client->client_used_pages_list 403 : NULL); 404 405 set_helper_priority(env, &curr_client->thread, SERSERV_TEST_PRIO_CLIENT); 406 start_helper(env, &curr_client->thread, &client_common_entry_point, 407 (seL4_Word)test_fn, 408 (seL4_Word)used_pages_list, 409 curr_client->n_used_pages, 410 i); 411 } 412} 413 414static void cleanup_clients(struct env *env) 415{ 416 for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { 417 helper_thread_t *curr_client = &client_globals.client_test_data[i].thread; 418 cleanup_helper(env, curr_client); 419 } 420} 421 422/** Compiles a list of pages that libsel4utils allocated while creating a 423 * thread in a new VSpace. 424 * 425 * This is useful if you want your new process to be able to initialize its own 426 * vspace_t, because you will need to have a list of pages that are occupied in 427 * the new VSpace, in order to get a usable vspace_t for that VSpace. 428 * You need to allocate the memory for page_list yourself. 429 * 430 * @param page_list The base vaddr of a page in the PARENT VSpace (the thread 431 * that is spawning the new thread), that will be used as the 432 * shared mem for telling the new thread which pages in its 433 * VSpace are occupied. 434 * @param p Initialized sel4utils_process_t handle for the new thread. 435 * @return 0 on success; Positive integer count of the number of used pages 436 * whose vaddrs were filled into the page_list on success. 437 */ 438static int fill_sel4utils_used_pages_list(volatile void **page_list, sel4utils_process_t *p) 439{ 440 seL4_Word sel4utils_stack_n_pages, sel4utils_stack_base_vaddr; 441 442 if (page_list == NULL || p == NULL) { 443 return -1; 444 } 445 446 /* Need to fill in the vaddrs for its stack and IPC buffer. 447 */ 448 449 /* libsel4utils adds another page for guard */ 450 sel4utils_stack_n_pages = BYTES_TO_4K_PAGES(CONFIG_SEL4UTILS_STACK_SIZE) + 1; 451 sel4utils_stack_base_vaddr = (uintptr_t)p->thread.stack_top - (sel4utils_stack_n_pages * BIT(seL4_PageBits)); 452 for (int i = 0; i < sel4utils_stack_n_pages; i++) { 453 *page_list = (void *)sel4utils_stack_base_vaddr; 454 sel4utils_stack_base_vaddr += BIT(seL4_PageBits); 455 page_list++; 456 } 457 458 /* Next take care of the IPC buffer */ 459 *page_list = (void *)p->thread.ipc_buffer_addr; 460 page_list++; 461 /* NULL terminate the list */ 462 *page_list = NULL; 463 464 /* Stack pages + IPC buffer */ 465 return sel4utils_stack_n_pages + 1; 466} 467 468/** Shares a used page list to a target process that was created by sel4utils. 469 * 470 * @param page_list Page that has been filled out with a list of pages used by 471 * sel4utils_configure_process. 472 * @param n_entries Number of entries in the page_list. 473 * @param from_vspace Initialized vspace_t for the parent process (that is doing 474 * the spawning). 475 * @param p Initialized sel4utils_process_t for the new process. 476 * @param target_vaddr[out] The vaddr that the NEW process can access the 477 * used pages list at, within ITS own VSpace. 478 * @return Negative integer on failure. Positive integer on success, which 479 * represents the number of ADDITIONAL used pages filled in by this 480 * function invocation. 481 */ 482static int share_sel4utils_used_pages_list(volatile void **page_list, size_t n_entries, 483 vspace_t *from_vspace, 484 sel4utils_process_t *p, 485 volatile void **target_vaddr) 486{ 487 vspace_t *to_vspace; 488 489 if (page_list == NULL || from_vspace == NULL || p == NULL 490 || target_vaddr == NULL) { 491 return -1; 492 } 493 494 to_vspace = &p->vspace; 495 *target_vaddr = vspace_share_mem(from_vspace, to_vspace, 496 (void *)ALIGN_DOWN((uintptr_t)page_list, BIT(seL4_PageBits)), 497 1, seL4_PageBits, 498 seL4_AllRights, true); 499 if (*target_vaddr == NULL) { 500 return -1; 501 } 502 /* Don't forget to add the offset back if the page_list vaddr was not 503 * page aligned 504 */ 505 *target_vaddr = (void *)((uintptr_t) * target_vaddr 506 + ((uintptr_t)page_list & (BIT(seL4_PageBits) - 1))); 507 /* Next, take care of the shmem page we ourselves created in this 508 * function 509 */ 510 page_list[n_entries] = *target_vaddr; 511 return 1; 512} 513 514/** For each new client process, fill out a list of virtual pages that will be 515 * occupied in its VSpace before it even begins executing. 516 * 517 * The client will use this list to initialize a vspace_t instance. This is 518 * called in the Parent, before the clients are allowed to execute. 519 * @return 0 on success; non-zero on error. 520 */ 521static int setup_pclient_used_pages_lists(struct env *env) 522{ 523 int error; 524 525 for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { 526 client_test_data_t *curr_client = &client_globals.client_test_data[i]; 527 528 /* Allocate the page in our VSpace */ 529 curr_client->parent_used_pages_list = (volatile void **)vspace_new_pages( 530 &env->vspace, 531 seL4_AllRights, 532 1, seL4_PageBits); 533 if (curr_client->parent_used_pages_list == NULL) { 534 return -1; 535 } 536 537 /* Give the page to fill_sel4utils_used_pages and let it fill out the 538 * pages it used in the new process' VSpace. 539 */ 540 curr_client->n_used_pages = fill_sel4utils_used_pages_list( 541 curr_client->parent_used_pages_list, 542 &curr_client->thread.process); 543 if (curr_client->n_used_pages < 0) { 544 return -1; 545 } 546 547 /* Share the page containing the used pages list with the new process */ 548 error = share_sel4utils_used_pages_list(curr_client->parent_used_pages_list, 549 curr_client->n_used_pages, 550 &env->vspace, 551 &curr_client->thread.process, 552 &curr_client->client_used_pages_list); 553 if (error < 0) { 554 return -1; 555 } 556 curr_client->n_used_pages += error; 557 /* NULL terminate the list since we added to it. */ 558 curr_client->parent_used_pages_list[curr_client->n_used_pages] = NULL; 559 } 560 return 0; 561} 562 563/** The new client processes each need an untyped of some size so they can 564 * allocate memory. 565 * 566 * 512k seems reasonably large (if not a little too large). Allocate an untyped 567 * for each new client process. This is called in the parent, before the test 568 * clients are allowed to execute. 569 * @return 0 on success; Non-zero on error. 570 */ 571static int alloc_untypeds_for_clients(struct env *env) 572{ 573 int error; 574 575 for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { 576 client_test_data_t *curr_client = &client_globals.client_test_data[i]; 577 vka_object_t tmp; 578 579 error = vka_alloc_untyped(&env->vka, BYTES_TO_SIZE_BITS(SERSERV_TEST_UT_SIZE), &tmp); 580 if (error != 0) { 581 return error; 582 } 583 584 vka_cspace_make_path(&env->vka, tmp.cptr, &curr_client->ut_512k_cspath); 585 curr_client->ut_512k_cspath.capPtr = sel4utils_move_cap_to_process( 586 &curr_client->thread.process, 587 curr_client->ut_512k_cspath, 588 &env->vka); 589 if (curr_client->ut_512k_cspath.capPtr == 0) { 590 return -1; 591 } 592 } 593 return 0; 594} 595 596static void copy_client_data_to_shmem(struct env *env) 597{ 598 for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { 599 client_test_data_t *curr_client = &client_globals.client_test_data[i]; 600 /* +1 because the list is NULL terminated and we want to get past the 601 * end of the list. 602 */ 603 void *client_data_copy = &curr_client->parent_used_pages_list[ 604 curr_client->n_used_pages + 1]; 605 606 memcpy(client_data_copy, curr_client, sizeof(*curr_client)); 607 } 608} 609 610static int concurrency_test_common(struct env *env, bool is_process, client_test_fn *test_fn, bool mint_2nd_ep_cap) 611{ 612 int error; 613 614 error = serial_server_parent_spawn_thread(&env->simple, 615 &env->vka, &env->vspace, 616 SERSERV_TEST_PRIO_SERVER); 617 618 test_eq(error, 0); 619 620 init_file_globals(env); 621 create_clients(env, is_process); 622 error = mint_server_ep_to_clients(env, mint_2nd_ep_cap); 623 test_eq(error, 0); 624 625 if (is_process) { 626 /* Setup the extra things that a new VSpace/CSpace needs. 627 */ 628 error = setup_pclient_used_pages_lists(env); 629 test_eq(error, 0); 630 error = alloc_untypeds_for_clients(env); 631 test_eq(error, 0); 632 copy_client_data_to_shmem(env); 633 }; 634 635 start_clients(env, test_fn); 636 637 for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { 638 error = wait_for_helper(&client_globals.client_test_data[i].thread); 639 test_eq(error, 0); 640 } 641 642 cleanup_clients(env); 643 return 0; 644} 645 646static int test_client_connect(struct env *env) 647{ 648 int error; 649 650 error = concurrency_test_common(env, false, &client_connect_main, false); 651 test_eq(error, 0); 652 return sel4test_get_result(); 653} 654DEFINE_TEST(SERSERV_CLIENT_001, "Connect from client threads", 655 test_client_connect, true) 656 657static int 658test_client_printf(struct env *env) 659{ 660 int error; 661 662 error = concurrency_test_common(env, false, &client_printf_main, false); 663 test_eq(error, 0); 664 return sel4test_get_result(); 665} 666DEFINE_TEST(SERSERV_CLIENT_002, "Printf from client threads", 667 test_client_printf, true) 668 669static int 670test_client_write(struct env *env) 671{ 672 int error; 673 674 error = concurrency_test_common(env, false, &client_write_main, false); 675 test_eq(error, 0); 676 return sel4test_get_result(); 677} 678DEFINE_TEST(SERSERV_CLIENT_003, "Write from client threads", 679 test_client_write, true) 680 681static int 682test_client_disconnect_reconnect_printf_write(struct env *env) 683{ 684 int error; 685 686 error = concurrency_test_common(env, false, &client_disconnect_reconnect_printf_write_main, true); 687 test_eq(error, 0); 688 return sel4test_get_result(); 689} 690DEFINE_TEST(SERSERV_CLIENT_004, "Printf, then write, then reset connection, and " 691 "Printf, then write again, from client threads", 692 test_client_disconnect_reconnect_printf_write, true) 693 694static int 695test_client_kill(struct env *env) 696{ 697 int error; 698 cspacepath_t parent_badged_server_ep_cspath; 699 serial_client_context_t conn; 700 701 error = concurrency_test_common(env, false, &client_disconnect_main, false); 702 test_eq(error, 0); 703 704 error = serial_server_parent_vka_mint_endpoint(&env->vka, 705 &parent_badged_server_ep_cspath); 706 test_eq(error, 0); 707 708 error = serial_server_client_connect(parent_badged_server_ep_cspath.capPtr, 709 &env->vka, &env->vspace, &conn); 710 test_eq(error, 0); 711 712 error = serial_server_kill(&conn); 713 test_eq(error, 0); 714 return sel4test_get_result(); 715} 716DEFINE_TEST(SERSERV_CLIENT_005, "Connect, then disconnect from server on all " 717 "threads, then kill server from parent thread", 718 test_client_kill, true) 719 720static int 721test_client_process_connect(struct env *env) 722{ 723 int error; 724 725 error = concurrency_test_common(env, true, &client_connect_main, false); 726 test_eq(error, 0); 727 return sel4test_get_result(); 728} 729DEFINE_TEST(SERSERV_CLI_PROC_001, "Connect to server from a client in another " 730 "VSpace and CSpace", test_client_process_connect, true) 731 732static int 733test_client_process_printf(struct env *env) 734{ 735 int error; 736 737 error = concurrency_test_common(env, true, &client_printf_main, false); 738 test_eq(error, 0); 739 return sel4test_get_result(); 740} 741DEFINE_TEST(SERSERV_CLI_PROC_002, "Connect to server and printf(, true), from a client " 742 "in another VSpace and CSpace", test_client_process_printf, true) 743 744static int 745test_client_process_write(struct env *env) 746{ 747 int error; 748 749 error = concurrency_test_common(env, true, &client_write_main, false); 750 test_eq(error, 0); 751 return sel4test_get_result(); 752} 753DEFINE_TEST(SERSERV_CLI_PROC_003, "Connect to server and write(, true), from a client " 754 "in another VSpace and CSpace", test_client_process_write, true) 755 756static int 757test_client_process_disconnect_reconnect_printf_write(struct env *env) 758{ 759 int error; 760 761 error = concurrency_test_common(env, true, 762 &client_disconnect_reconnect_printf_write_main, 763 true); 764 test_eq(error, 0); 765 return sel4test_get_result(); 766} 767DEFINE_TEST(SERSERV_CLI_PROC_004, "Connect to server, printf(), write(, true), then " 768 "disconnect, then reconnect and printf() and write() again, from " 769 "clients in other VSpaces and CSpaces", 770 test_client_process_disconnect_reconnect_printf_write, true) 771 772static int 773test_client_process_kill(struct env *env) 774{ 775 int error; 776 serial_client_context_t conn; 777 cspacepath_t badged_server_ep_cspath; 778 779 error = concurrency_test_common(env, true, &client_disconnect_main, false); 780 test_eq(error, 0); 781 782 error = serial_server_parent_vka_mint_endpoint(&env->vka, &badged_server_ep_cspath); 783 test_eq(error, 0); 784 785 error = serial_server_client_connect(badged_server_ep_cspath.capPtr, 786 &env->vka, &env->vspace, &conn); 787 test_eq(error, 0); 788 error = serial_server_kill(&conn); 789 test_eq(error, 0); 790 return sel4test_get_result(); 791} 792DEFINE_TEST(SERSERV_CLI_PROC_005, "Connect to server then disconnect on all " 793 "clients (in different VSpace/CSpace containers), and finally kill " 794 "the server from the parent", 795 test_client_process_kill, true) 796