| /* |
| * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| */ |
| #include <string.h> |
| #include <stdio.h> |
| |
| #include <sel4/sel4.h> |
| #include <vka/capops.h> |
| #include <allocman/allocman.h> |
| #include <allocman/vka.h> |
| #include <allocman/bootstrap.h> |
| #include <sel4utils/thread.h> |
| #include <serial_server/parent.h> |
| #include <serial_server/client.h> |
| |
| #include "../test.h" |
| #include "../helpers.h" |
| |
| #define SERSERV_TEST_PRIO_SERVER (seL4_MaxPrio - 1) |
| #define SERSERV_TEST_PRIO_CLIENT (seL4_MinPrio) |
| |
| #define SERSERV_TEST_N_CLIENTS (1) |
| |
| #define SERSERV_TEST_ALLOCMAN_PREALLOCATED_MEMSIZE (64 * 1024) |
| #define SERSERV_TEST_UT_SIZE (512 * 1024) |
| |
| static const char *test_str = "Hello, world!\n"; |
| |
| |
| /* These next few tests test the same things from client threads. |
| * |
| * We spawn a bunch of helper threads and then ask those helper threads to |
| * run these same tests, and then have the helpers return the values they |
| * got when they called the Server. |
| * |
| * We keep it simple as well by using some global state to pass data to the |
| * client threads so they can do their work. Then, so that each client knows |
| * which one it is, we pass the clients a numeric ID invented by the test |
| * to identify that client. |
| */ |
| typedef struct _client_test_data { |
| helper_thread_t thread; |
| /* "badged_server_ep_cspath2" is used in the disconnect/reconnect tests: |
| * the client can't reconnect using the same badged ep as the first |
| * connection. |
| * |
| * We could make each client seL4_Call() the main thread and ask |
| * it to re-mint the ep-cap to each client, but that imposes extra |
| * complexity for a simple test. So for that particular test we just mint 2 |
| * ep caps from the start. |
| */ |
| volatile void **parent_used_pages_list, *client_used_pages_list; |
| int n_used_pages; |
| cspacepath_t badged_server_ep_cspath, badged_server_ep_cspath2, ut_512k_cspath; |
| serial_client_context_t conn; |
| } client_test_data_t; |
| |
| typedef int (client_test_fn)(client_test_data_t *, vka_t *, vspace_t *); |
| |
| struct { |
| vka_t *vka; |
| vspace_t *vspace; |
| simple_t *simple; |
| client_test_data_t client_test_data[SERSERV_TEST_N_CLIENTS]; |
| uint8_t allocman_mem[SERSERV_TEST_N_CLIENTS][SERSERV_TEST_ALLOCMAN_PREALLOCATED_MEMSIZE]; |
| } static client_globals; |
| |
| /* These next few functions (client_*_main())are the actual tests that are run |
| * by the client threads and processes for the concurrency tests. |
| */ |
| |
| /** This function will guide a client thread to connect to the server and return the |
| * result. |
| */ |
| static int client_connect_main(client_test_data_t *self, vka_t *vka, vspace_t *vspace) |
| { |
| int error = 0; |
| serial_client_context_t conn; |
| |
| error = serial_server_client_connect(self->badged_server_ep_cspath.capPtr, |
| vka, vspace, |
| &conn); |
| return error; |
| } |
| |
| /** This function will guide a client thread to connect to the server, send a |
| * printf() request, and return the result. |
| */ |
| static int client_printf_main(client_test_data_t *self, vka_t *vka, vspace_t *vspace) |
| { |
| int error; |
| serial_client_context_t conn; |
| |
| error = serial_server_client_connect(self->badged_server_ep_cspath.capPtr, |
| vka, vspace, |
| &conn); |
| if (error != 0) { |
| return error; |
| } |
| |
| error = serial_server_printf(&conn, test_str); |
| if (error != strlen(test_str)) { |
| return -1; |
| } |
| return 0; |
| } |
| |
| /** This function will guide a client thread to connect to the server, send a |
| * write() request, and return the result. |
| */ |
| static int client_write_main(client_test_data_t *self, vka_t *vka, vspace_t *vspace) |
| { |
| int error; |
| serial_client_context_t conn; |
| |
| error = serial_server_client_connect(self->badged_server_ep_cspath.capPtr, |
| vka, vspace, |
| &conn); |
| if (error != 0) { |
| return error; |
| } |
| |
| error = serial_server_write(&conn, test_str, strlen(test_str)); |
| if (error != strlen(test_str)) { |
| return -1; |
| } |
| return 0; |
| } |
| |
| /** This function will guide a client thread to connect to the server, send a |
| * printf() request, then a write() request, then disconnect(), and then |
| * reconnect to the Server and send another printf() and another write(), |
| * and return the result. |
| */ |
| static int client_disconnect_reconnect_printf_write_main(client_test_data_t *self, vka_t *vka, vspace_t *vspace) |
| { |
| int error; |
| serial_client_context_t conn; |
| |
| error = serial_server_client_connect(self->badged_server_ep_cspath.capPtr, |
| vka, vspace, |
| &conn); |
| if (error != 0) { |
| return error; |
| } |
| error = serial_server_printf(&conn, test_str); |
| if (error != strlen(test_str)) { |
| return -1; |
| } |
| error = serial_server_write(&conn, test_str, strlen(test_str)); |
| if (error != strlen(test_str)) { |
| return error; |
| } |
| |
| serial_server_disconnect(&conn); |
| |
| error = serial_server_client_connect(self->badged_server_ep_cspath2.capPtr, |
| vka, vspace, |
| &conn); |
| if (error != 0) { |
| return error; |
| } |
| error = serial_server_printf(&conn, test_str); |
| if (error != strlen(test_str)) { |
| return -1; |
| } |
| error = serial_server_write(&conn, test_str, strlen(test_str)); |
| if (error != strlen(test_str)) { |
| return -1; |
| } |
| return 0; |
| } |
| |
| /** This function will guide a client thread to connect to the server, and then |
| * send a kill() request. |
| */ |
| static int client_disconnect_main(client_test_data_t *self, vka_t *vka, vspace_t *vspace) |
| { |
| int error; |
| serial_client_context_t conn; |
| |
| error = serial_server_client_connect(self->badged_server_ep_cspath.capPtr, |
| vka, vspace, |
| &conn); |
| if (error != 0) { |
| return error; |
| } |
| |
| serial_server_disconnect(&conn); |
| return 0; |
| } |
| |
| static void init_file_globals(struct env *env) |
| { |
| memset(client_globals.client_test_data, 0, |
| sizeof(client_globals.client_test_data)); |
| |
| memset(client_globals.allocman_mem, 0, sizeof(client_globals.allocman_mem)); |
| |
| client_globals.vka = &env->vka; |
| client_globals.vspace = &env->vspace; |
| client_globals.simple = &env->simple; |
| } |
| |
| static int mint_server_ep_to_clients(struct env *env, bool mint_2nd_ep) |
| { |
| int error; |
| |
| for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { |
| /* Ask the serserv library to mint the Server's EP to all the new clients. */ |
| client_test_data_t *curr_client = &client_globals.client_test_data[i]; |
| |
| if (curr_client->thread.is_process) { |
| curr_client->badged_server_ep_cspath.capPtr = serial_server_parent_mint_endpoint_to_process( |
| &curr_client->thread.process); |
| if (curr_client->badged_server_ep_cspath.capPtr == 0) { |
| return -1; |
| } |
| } else { |
| error = serial_server_parent_vka_mint_endpoint(&env->vka, |
| &curr_client->badged_server_ep_cspath); |
| if (error != 0) { |
| return -1; |
| } |
| } |
| |
| if (mint_2nd_ep) { |
| if (curr_client->thread.is_process) { |
| curr_client->badged_server_ep_cspath2.capPtr = serial_server_parent_mint_endpoint_to_process( |
| &curr_client->thread.process); |
| if (curr_client->badged_server_ep_cspath2.capPtr == 0) { |
| return -1; |
| } |
| } else { |
| error = serial_server_parent_vka_mint_endpoint(&env->vka, |
| &curr_client->badged_server_ep_cspath2); |
| if (error != 0) { |
| return -1; |
| } |
| } |
| } |
| } |
| return 0; |
| } |
| |
| static void create_clients(struct env *env, bool is_process) |
| { |
| for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { |
| client_test_data_t *curr_client = &client_globals.client_test_data[i]; |
| |
| if (is_process) { |
| create_helper_process(env, &curr_client->thread); |
| } else { |
| create_helper_thread(env, &curr_client->thread); |
| } |
| } |
| } |
| |
| /** Guides a client "process" to set up a vspace_t instance for its new VSpace. |
| * |
| * This basically entails setting up an allocman_t instance, followed by a |
| * vka_t instance and finally a vspace_t instance. The function requires an |
| * untyped of a "reasonable" size (you determine this based on what this new |
| * process will be doing), a list of used pages in the new process' VSpace, |
| * some allocman initial mem and your pointers. |
| * |
| * @param ut_cap CPtr to an untyped of "reasonable" size that was copied to the |
| * new process before this function was invoked. |
| * @param ut_size_bits The size in bits of the untyped "ut_cap". |
| * @param used_pages_list A NULL-terminated list of virtual pages that are |
| * already occupied within this new process' VSpace. |
| * @param allocman_mem Initial memory set aside for bootstrapping an allocman_t. |
| * @param allocman_mem_size Size of the initial allocman_t memory. |
| * @param allocman[out] Pointer to a pointer to an uninitialized allocman_t |
| * instance. Will be returned with an initialized |
| * allocman_t if this function completes successfully. |
| * @param vka[out] Pointer to an uninitialized vka_t instance. |
| * @param vspace[out] Pointer to an uninitialized vspace_t instance. |
| * @param alloc_data[out] Pointer to an uninitialized alloc data structure. |
| * @return 0 on success; non-zero on error. |
| */ |
| static int setup_client_process_allocman_vka_and_vspace(seL4_CPtr ut_cap, size_t ut_size_bits, |
| seL4_CPtr first_free_cptr, |
| volatile void **used_pages_list, |
| uint8_t *allocman_mem, size_t allocman_mem_size, |
| allocman_t **allocman, |
| vka_t *vka, vspace_t *vspace, |
| sel4utils_alloc_data_t *alloc_data) |
| { |
| cspacepath_t ut_cspath; |
| uintptr_t paddr; |
| int error; |
| |
| if (used_pages_list == NULL || allocman_mem == NULL |
| || allocman == NULL || vspace == NULL || vka == NULL |
| || alloc_data == NULL || allocman_mem_size == 0) { |
| return seL4_InvalidArgument; |
| } |
| *allocman = bootstrap_use_current_1level(SEL4UTILS_CNODE_SLOT, |
| TEST_PROCESS_CSPACE_SIZE_BITS, |
| first_free_cptr, |
| BIT(TEST_PROCESS_CSPACE_SIZE_BITS), |
| allocman_mem_size, |
| allocman_mem); |
| if (*allocman == NULL) { |
| return -1; |
| } |
| |
| allocman_make_vka(vka, *allocman); |
| vka_cspace_make_path(vka, ut_cap, &ut_cspath); |
| error = allocman_utspace_add_uts(*allocman, 1, &ut_cspath, |
| &ut_size_bits, |
| &paddr, ALLOCMAN_UT_KERNEL); |
| if (error != 0) { |
| return error; |
| } |
| |
| /* Pass the shmem page containing the list of already allocated frames, to |
| * the vspace bootstrap. |
| */ |
| return sel4utils_bootstrap_vspace_leaky(vspace, alloc_data, |
| SEL4UTILS_PD_SLOT, vka, |
| (void **)used_pages_list); |
| } |
| |
| /** Common entry point for all client thread and processes alike. |
| * |
| * This is what is passed to start_helper so that it can do its init work. |
| * This function invokes the actual test routine after it finishes working. |
| * |
| * Determines whether the new client is a thread or process. If it's a process |
| * we do some extra processing, vis-a-vis setting up the new process' own |
| * vka, vspace and allocman, so it can call the library. |
| * |
| * @param _test_fn The actual test function that is to be invoked. |
| * @param _used_pages_list List of used virtual pages in the VSpace of the new |
| * client. This is used to initialize the new client's |
| * vspace_t instance. |
| * @param n_used_pages Number of elements in the _used_pages_list. |
| * @param thread_num The "ID" of this thread as the parent assigned it. |
| * @return 0 on success; non-zero on error. |
| */ |
| static int client_common_entry_point(seL4_Word _test_fn, seL4_Word _used_pages_list, |
| seL4_Word n_used_pages, seL4_Word thread_num) |
| { |
| bool is_process; |
| client_test_data_t *client_data; |
| volatile void **used_pages_list = (volatile void **)_used_pages_list; |
| client_test_fn *test_fn = (client_test_fn *)_test_fn; |
| int error; |
| vka_t *vka, _vka; |
| vspace_t *vspace, _vspace; |
| sel4utils_alloc_data_t alloc_data; |
| allocman_t *allocman; |
| |
| is_process = used_pages_list != NULL; |
| |
| if (is_process) { |
| /* +1 because the list is NULL terminated and we want to get past the |
| * end of the list. |
| */ |
| client_data = (client_test_data_t *)&used_pages_list[n_used_pages + 1]; |
| vka = &_vka; |
| vspace = &_vspace; |
| error = setup_client_process_allocman_vka_and_vspace(client_data->ut_512k_cspath.capPtr, |
| BYTES_TO_SIZE_BITS(SERSERV_TEST_UT_SIZE), |
| client_data->ut_512k_cspath.capPtr + 0x8, |
| used_pages_list, |
| client_globals.allocman_mem[thread_num], |
| SERSERV_TEST_ALLOCMAN_PREALLOCATED_MEMSIZE, |
| &allocman, |
| vka, vspace, |
| &alloc_data); |
| test_eq(error, 0); |
| } else { |
| client_data = &client_globals.client_test_data[thread_num]; |
| vka = client_globals.vka; |
| vspace = client_globals.vspace; |
| } |
| return test_fn(client_data, vka, vspace); |
| } |
| |
| static void start_clients(struct env *env, client_test_fn *test_fn) |
| { |
| bool is_process = client_globals.client_test_data[0].thread.is_process; |
| |
| for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { |
| client_test_data_t *curr_client = &client_globals.client_test_data[i]; |
| /* The client threads don't need a list of used pages. The client |
| * processes do need it -- so if the used pages list is NULL, the newly |
| * spawned client assumes it's a thread and not a process. |
| */ |
| volatile void **used_pages_list = ((is_process) |
| ? (volatile void **)curr_client->client_used_pages_list |
| : NULL); |
| |
| set_helper_priority(env, &curr_client->thread, SERSERV_TEST_PRIO_CLIENT); |
| start_helper(env, &curr_client->thread, &client_common_entry_point, |
| (seL4_Word)test_fn, |
| (seL4_Word)used_pages_list, |
| curr_client->n_used_pages, |
| i); |
| } |
| } |
| |
| static void cleanup_clients(struct env *env) |
| { |
| for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { |
| helper_thread_t *curr_client = &client_globals.client_test_data[i].thread; |
| cleanup_helper(env, curr_client); |
| } |
| } |
| |
| /** Compiles a list of pages that libsel4utils allocated while creating a |
| * thread in a new VSpace. |
| * |
| * This is useful if you want your new process to be able to initialize its own |
| * vspace_t, because you will need to have a list of pages that are occupied in |
| * the new VSpace, in order to get a usable vspace_t for that VSpace. |
| * You need to allocate the memory for page_list yourself. |
| * |
| * @param page_list The base vaddr of a page in the PARENT VSpace (the thread |
| * that is spawning the new thread), that will be used as the |
| * shared mem for telling the new thread which pages in its |
| * VSpace are occupied. |
| * @param p Initialized sel4utils_process_t handle for the new thread. |
| * @return 0 on success; Positive integer count of the number of used pages |
| * whose vaddrs were filled into the page_list on success. |
| */ |
| static int fill_sel4utils_used_pages_list(volatile void **page_list, sel4utils_process_t *p) |
| { |
| seL4_Word sel4utils_stack_n_pages, sel4utils_stack_base_vaddr; |
| |
| if (page_list == NULL || p == NULL) { |
| return -1; |
| } |
| |
| /* Need to fill in the vaddrs for its stack and IPC buffer. |
| */ |
| |
| /* libsel4utils adds another page for guard */ |
| sel4utils_stack_n_pages = BYTES_TO_4K_PAGES(CONFIG_SEL4UTILS_STACK_SIZE) + 1; |
| sel4utils_stack_base_vaddr = (uintptr_t)p->thread.stack_top - (sel4utils_stack_n_pages * BIT(seL4_PageBits)); |
| for (int i = 0; i < sel4utils_stack_n_pages; i++) { |
| *page_list = (void *)sel4utils_stack_base_vaddr; |
| sel4utils_stack_base_vaddr += BIT(seL4_PageBits); |
| page_list++; |
| } |
| |
| /* Next take care of the IPC buffer */ |
| *page_list = (void *)p->thread.ipc_buffer_addr; |
| page_list++; |
| /* NULL terminate the list */ |
| *page_list = NULL; |
| |
| /* Stack pages + IPC buffer */ |
| return sel4utils_stack_n_pages + 1; |
| } |
| |
| /** Shares a used page list to a target process that was created by sel4utils. |
| * |
| * @param page_list Page that has been filled out with a list of pages used by |
| * sel4utils_configure_process. |
| * @param n_entries Number of entries in the page_list. |
| * @param from_vspace Initialized vspace_t for the parent process (that is doing |
| * the spawning). |
| * @param p Initialized sel4utils_process_t for the new process. |
| * @param target_vaddr[out] The vaddr that the NEW process can access the |
| * used pages list at, within ITS own VSpace. |
| * @return Negative integer on failure. Positive integer on success, which |
| * represents the number of ADDITIONAL used pages filled in by this |
| * function invocation. |
| */ |
| static int share_sel4utils_used_pages_list(volatile void **page_list, size_t n_entries, |
| vspace_t *from_vspace, |
| sel4utils_process_t *p, |
| volatile void **target_vaddr) |
| { |
| vspace_t *to_vspace; |
| |
| if (page_list == NULL || from_vspace == NULL || p == NULL |
| || target_vaddr == NULL) { |
| return -1; |
| } |
| |
| to_vspace = &p->vspace; |
| *target_vaddr = vspace_share_mem(from_vspace, to_vspace, |
| (void *)ALIGN_DOWN((uintptr_t)page_list, BIT(seL4_PageBits)), |
| 1, seL4_PageBits, |
| seL4_AllRights, true); |
| if (*target_vaddr == NULL) { |
| return -1; |
| } |
| /* Don't forget to add the offset back if the page_list vaddr was not |
| * page aligned |
| */ |
| *target_vaddr = (void *)((uintptr_t) * target_vaddr |
| + ((uintptr_t)page_list & (BIT(seL4_PageBits) - 1))); |
| /* Next, take care of the shmem page we ourselves created in this |
| * function |
| */ |
| page_list[n_entries] = *target_vaddr; |
| return 1; |
| } |
| |
| /** For each new client process, fill out a list of virtual pages that will be |
| * occupied in its VSpace before it even begins executing. |
| * |
| * The client will use this list to initialize a vspace_t instance. This is |
| * called in the Parent, before the clients are allowed to execute. |
| * @return 0 on success; non-zero on error. |
| */ |
| static int setup_pclient_used_pages_lists(struct env *env) |
| { |
| int error; |
| |
| for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { |
| client_test_data_t *curr_client = &client_globals.client_test_data[i]; |
| |
| /* Allocate the page in our VSpace */ |
| curr_client->parent_used_pages_list = (volatile void **)vspace_new_pages( |
| &env->vspace, |
| seL4_AllRights, |
| 1, seL4_PageBits); |
| if (curr_client->parent_used_pages_list == NULL) { |
| return -1; |
| } |
| |
| /* Give the page to fill_sel4utils_used_pages and let it fill out the |
| * pages it used in the new process' VSpace. |
| */ |
| curr_client->n_used_pages = fill_sel4utils_used_pages_list( |
| curr_client->parent_used_pages_list, |
| &curr_client->thread.process); |
| if (curr_client->n_used_pages < 0) { |
| return -1; |
| } |
| |
| /* Share the page containing the used pages list with the new process */ |
| error = share_sel4utils_used_pages_list(curr_client->parent_used_pages_list, |
| curr_client->n_used_pages, |
| &env->vspace, |
| &curr_client->thread.process, |
| &curr_client->client_used_pages_list); |
| if (error < 0) { |
| return -1; |
| } |
| curr_client->n_used_pages += error; |
| /* NULL terminate the list since we added to it. */ |
| curr_client->parent_used_pages_list[curr_client->n_used_pages] = NULL; |
| } |
| return 0; |
| } |
| |
| /** The new client processes each need an untyped of some size so they can |
| * allocate memory. |
| * |
| * 512k seems reasonably large (if not a little too large). Allocate an untyped |
| * for each new client process. This is called in the parent, before the test |
| * clients are allowed to execute. |
| * @return 0 on success; Non-zero on error. |
| */ |
| static int alloc_untypeds_for_clients(struct env *env) |
| { |
| int error; |
| |
| for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { |
| client_test_data_t *curr_client = &client_globals.client_test_data[i]; |
| vka_object_t tmp; |
| |
| error = vka_alloc_untyped(&env->vka, BYTES_TO_SIZE_BITS(SERSERV_TEST_UT_SIZE), &tmp); |
| if (error != 0) { |
| return error; |
| } |
| |
| vka_cspace_make_path(&env->vka, tmp.cptr, &curr_client->ut_512k_cspath); |
| curr_client->ut_512k_cspath.capPtr = sel4utils_move_cap_to_process( |
| &curr_client->thread.process, |
| curr_client->ut_512k_cspath, |
| &env->vka); |
| if (curr_client->ut_512k_cspath.capPtr == 0) { |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| static void copy_client_data_to_shmem(struct env *env) |
| { |
| for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { |
| client_test_data_t *curr_client = &client_globals.client_test_data[i]; |
| /* +1 because the list is NULL terminated and we want to get past the |
| * end of the list. |
| */ |
| void *client_data_copy = &curr_client->parent_used_pages_list[ |
| curr_client->n_used_pages + 1]; |
| |
| memcpy(client_data_copy, curr_client, sizeof(*curr_client)); |
| } |
| } |
| |
| static int concurrency_test_common(struct env *env, bool is_process, client_test_fn *test_fn, bool mint_2nd_ep_cap) |
| { |
| int error; |
| |
| error = serial_server_parent_spawn_thread(&env->simple, |
| &env->vka, &env->vspace, |
| SERSERV_TEST_PRIO_SERVER); |
| |
| test_eq(error, 0); |
| |
| init_file_globals(env); |
| create_clients(env, is_process); |
| error = mint_server_ep_to_clients(env, mint_2nd_ep_cap); |
| test_eq(error, 0); |
| |
| if (is_process) { |
| /* Setup the extra things that a new VSpace/CSpace needs. |
| */ |
| error = setup_pclient_used_pages_lists(env); |
| test_eq(error, 0); |
| error = alloc_untypeds_for_clients(env); |
| test_eq(error, 0); |
| copy_client_data_to_shmem(env); |
| }; |
| |
| start_clients(env, test_fn); |
| |
| for (int i = 0; i < SERSERV_TEST_N_CLIENTS; i++) { |
| error = wait_for_helper(&client_globals.client_test_data[i].thread); |
| test_eq(error, 0); |
| } |
| |
| cleanup_clients(env); |
| return 0; |
| } |
| |
| static int test_client_connect(struct env *env) |
| { |
| int error; |
| |
| error = concurrency_test_common(env, false, &client_connect_main, false); |
| test_eq(error, 0); |
| return sel4test_get_result(); |
| } |
| DEFINE_TEST(SERSERV_CLIENT_001, "Connect from client threads", |
| test_client_connect, true) |
| |
| static int |
| test_client_printf(struct env *env) |
| { |
| int error; |
| |
| error = concurrency_test_common(env, false, &client_printf_main, false); |
| test_eq(error, 0); |
| return sel4test_get_result(); |
| } |
| DEFINE_TEST(SERSERV_CLIENT_002, "Printf from client threads", |
| test_client_printf, true) |
| |
| static int |
| test_client_write(struct env *env) |
| { |
| int error; |
| |
| error = concurrency_test_common(env, false, &client_write_main, false); |
| test_eq(error, 0); |
| return sel4test_get_result(); |
| } |
| DEFINE_TEST(SERSERV_CLIENT_003, "Write from client threads", |
| test_client_write, true) |
| |
| static int |
| test_client_disconnect_reconnect_printf_write(struct env *env) |
| { |
| int error; |
| |
| error = concurrency_test_common(env, false, &client_disconnect_reconnect_printf_write_main, true); |
| test_eq(error, 0); |
| return sel4test_get_result(); |
| } |
| DEFINE_TEST(SERSERV_CLIENT_004, "Printf, then write, then reset connection, and " |
| "Printf, then write again, from client threads", |
| test_client_disconnect_reconnect_printf_write, true) |
| |
| static int |
| test_client_kill(struct env *env) |
| { |
| int error; |
| cspacepath_t parent_badged_server_ep_cspath; |
| serial_client_context_t conn; |
| |
| error = concurrency_test_common(env, false, &client_disconnect_main, false); |
| test_eq(error, 0); |
| |
| error = serial_server_parent_vka_mint_endpoint(&env->vka, |
| &parent_badged_server_ep_cspath); |
| test_eq(error, 0); |
| |
| error = serial_server_client_connect(parent_badged_server_ep_cspath.capPtr, |
| &env->vka, &env->vspace, &conn); |
| test_eq(error, 0); |
| |
| error = serial_server_kill(&conn); |
| test_eq(error, 0); |
| return sel4test_get_result(); |
| } |
| DEFINE_TEST(SERSERV_CLIENT_005, "Connect, then disconnect from server on all " |
| "threads, then kill server from parent thread", |
| test_client_kill, true) |
| |
| static int |
| test_client_process_connect(struct env *env) |
| { |
| int error; |
| |
| error = concurrency_test_common(env, true, &client_connect_main, false); |
| test_eq(error, 0); |
| return sel4test_get_result(); |
| } |
| DEFINE_TEST(SERSERV_CLI_PROC_001, "Connect to server from a client in another " |
| "VSpace and CSpace", test_client_process_connect, true) |
| |
| static int |
| test_client_process_printf(struct env *env) |
| { |
| int error; |
| |
| error = concurrency_test_common(env, true, &client_printf_main, false); |
| test_eq(error, 0); |
| return sel4test_get_result(); |
| } |
| DEFINE_TEST(SERSERV_CLI_PROC_002, "Connect to server and printf(, true), from a client " |
| "in another VSpace and CSpace", test_client_process_printf, true) |
| |
| static int |
| test_client_process_write(struct env *env) |
| { |
| int error; |
| |
| error = concurrency_test_common(env, true, &client_write_main, false); |
| test_eq(error, 0); |
| return sel4test_get_result(); |
| } |
| DEFINE_TEST(SERSERV_CLI_PROC_003, "Connect to server and write(, true), from a client " |
| "in another VSpace and CSpace", test_client_process_write, true) |
| |
| static int |
| test_client_process_disconnect_reconnect_printf_write(struct env *env) |
| { |
| int error; |
| |
| error = concurrency_test_common(env, true, |
| &client_disconnect_reconnect_printf_write_main, |
| true); |
| test_eq(error, 0); |
| return sel4test_get_result(); |
| } |
| DEFINE_TEST(SERSERV_CLI_PROC_004, "Connect to server, printf(), write(, true), then " |
| "disconnect, then reconnect and printf() and write() again, from " |
| "clients in other VSpaces and CSpaces", |
| test_client_process_disconnect_reconnect_printf_write, true) |
| |
| static int |
| test_client_process_kill(struct env *env) |
| { |
| int error; |
| serial_client_context_t conn; |
| cspacepath_t badged_server_ep_cspath; |
| |
| error = concurrency_test_common(env, true, &client_disconnect_main, false); |
| test_eq(error, 0); |
| |
| error = serial_server_parent_vka_mint_endpoint(&env->vka, &badged_server_ep_cspath); |
| test_eq(error, 0); |
| |
| error = serial_server_client_connect(badged_server_ep_cspath.capPtr, |
| &env->vka, &env->vspace, &conn); |
| test_eq(error, 0); |
| error = serial_server_kill(&conn); |
| test_eq(error, 0); |
| return sel4test_get_result(); |
| } |
| DEFINE_TEST(SERSERV_CLI_PROC_005, "Connect to server then disconnect on all " |
| "clients (in different VSpace/CSpace containers), and finally kill " |
| "the server from the parent", |
| test_client_process_kill, true) |