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 <stdio.h> 13#include <string.h> 14#include <stdarg.h> 15#include <stdlib.h> 16#include <stdbool.h> 17 18#include <sel4/sel4.h> 19#include <sel4utils/strerror.h> 20#include <vka/vka.h> 21#include <vka/object.h> 22#include <vka/object_capops.h> 23 24#include "serial_server.h" 25#include <serial_server/parent.h> 26 27seL4_Error 28serial_server_parent_spawn_thread(simple_t *parent_simple, vka_t *parent_vka, 29 vspace_t *parent_vspace, 30 uint8_t priority) 31{ 32 const size_t shmem_max_size = SERIAL_SERVER_SHMEM_MAX_SIZE; 33 seL4_Error error; 34 size_t shmem_max_n_pages; 35 cspacepath_t parent_cspace_cspath; 36 seL4_MessageInfo_t tag; 37 38 if (parent_simple == NULL || parent_vka == NULL || parent_vspace == NULL) { 39 return seL4_InvalidArgument; 40 } 41 42 memset(get_serial_server(), 0, sizeof(serial_server_context_t)); 43 44 /* Get a CPtr to the parent's root cnode. */ 45 shmem_max_n_pages = BYTES_TO_4K_PAGES(shmem_max_size); 46 vka_cspace_make_path(parent_vka, 0, &parent_cspace_cspath); 47 48 get_serial_server()->server_vka = parent_vka; 49 get_serial_server()->server_vspace = parent_vspace; 50 get_serial_server()->server_cspace = parent_cspace_cspath.root; 51 get_serial_server()->server_simple = parent_simple; 52 53 /* Allocate the Endpoint that the server will be listening on. */ 54 error = vka_alloc_endpoint(parent_vka, &get_serial_server()->server_ep_obj); 55 if (error != 0) { 56 ZF_LOGE(SERSERVP"spawn_thread: failed to alloc endpoint, err=%d.", 57 error); 58 return error; 59 } 60 61 /* And also allocate a badged copy of the Server's endpoint that the Parent 62 * can use to send to the Server. This is used to allow the Server to report 63 * back to the Parent on whether or not the Server successfully bound to a 64 * platform serial driver. 65 * 66 * This badged endpoint will be reused by the library as the Parent's badged 67 * Endpoint cap, if the Parent itself ever chooses to connect() to the 68 * Server later on. 69 */ 70 71 get_serial_server()->parent_badge_value = serial_server_badge_value_alloc(); 72 if (get_serial_server()->parent_badge_value == SERIAL_SERVER_BADGE_VALUE_EMPTY) { 73 error = seL4_NotEnoughMemory; 74 goto out; 75 } 76 77 error = vka_mint_object(parent_vka, &get_serial_server()->server_ep_obj, 78 &get_serial_server()->_badged_server_ep_cspath, 79 seL4_AllRights, 80 get_serial_server()->parent_badge_value); 81 if (error != 0) { 82 ZF_LOGE(SERSERVP"spawn_thread: Failed to mint badged Endpoint cap to " 83 "server.\n" 84 "\tParent cannot confirm Server thread successfully spawned."); 85 goto out; 86 } 87 88 /* Allocate enough Cnode slots in our CSpace to enable us to receive 89 * frame caps from our clients, sufficient to cover "shmem_max_size". 90 * The problem here is that we're sort of forced to assume that we get 91 * these slots contiguously. If they're not, we have a problem. 92 * 93 * If a client tries to send us too many frames, we respond with an error, 94 * and indicate our shmem_max_size in the SSMSGREG_RESPONSE 95 * message register. 96 */ 97 get_serial_server()->frame_cap_recv_cspaths = calloc(shmem_max_n_pages, 98 sizeof(cspacepath_t)); 99 if (get_serial_server()->frame_cap_recv_cspaths == NULL) { 100 error = seL4_NotEnoughMemory; 101 goto out; 102 } 103 104 for (size_t i = 0; i < shmem_max_n_pages; i++) { 105 error = vka_cspace_alloc_path(parent_vka, 106 &get_serial_server()->frame_cap_recv_cspaths[i]); 107 if (error != 0) { 108 ZF_LOGE(SERSERVP"spawn_thread: Failed to alloc enough cnode slots " 109 "to receive shmem frame caps equal to %zd bytes.", 110 shmem_max_size); 111 goto out; 112 } 113 } 114 115 sel4utils_thread_config_t config = thread_config_default(parent_simple, parent_cspace_cspath.root, 116 seL4_NilData, get_serial_server()->server_ep_obj.cptr, priority); 117 error = sel4utils_configure_thread_config(parent_vka, parent_vspace, parent_vspace, 118 config, &get_serial_server()->server_thread); 119 if (error != 0) { 120 ZF_LOGE(SERSERVP"spawn_thread: sel4utils_configure_thread failed " 121 "with %d.", error); 122 goto out; 123 } 124 125 NAME_THREAD(get_serial_server()->server_thread.tcb.cptr, "serial server"); 126 error = sel4utils_start_thread(&get_serial_server()->server_thread, 127 (sel4utils_thread_entry_fn)&serial_server_main, 128 NULL, NULL, 1); 129 if (error != 0) { 130 ZF_LOGE(SERSERVP"spawn_thread: sel4utils_start_thread failed with " 131 "%d.", error); 132 goto out; 133 } 134 135 /* When the Server is spawned, it will reply to tell us whether or not it 136 * successfully bound itself to the platform serial device. Block here 137 * and wait for that reply. 138 */ 139 seL4_SetMR(SSMSGREG_FUNC, FUNC_SERVER_SPAWN_SYNC_REQ); 140 tag = seL4_MessageInfo_new(0, 0, 0, SSMSGREG_SPAWN_SYNC_REQ_END); 141 tag = seL4_Call(get_serial_server()->_badged_server_ep_cspath.capPtr, tag); 142 143 /* Did all go well with the server? */ 144 if (seL4_GetMR(SSMSGREG_FUNC) != FUNC_SERVER_SPAWN_SYNC_ACK) { 145 ZF_LOGE(SERSERVP"spawn_thread: Server thread sync message after spawn " 146 "was not a SYNC_ACK as expected."); 147 error = seL4_InvalidArgument; 148 goto out; 149 } 150 error = seL4_MessageInfo_get_label(tag); 151 if (error != 0) { 152 ZF_LOGE(SERSERVP"spawn_thread: Server thread failed to bind to the " 153 "platform serial device."); 154 goto out; 155 } 156 157 get_serial_server()->shmem_max_size = shmem_max_size; 158 get_serial_server()->shmem_max_n_pages = shmem_max_n_pages; 159 return 0; 160 161out: 162 if (get_serial_server()->frame_cap_recv_cspaths != NULL) { 163 for (size_t i = 0; i < shmem_max_n_pages; i++) { 164 /* Since the array was allocated with calloc(), it was zero'd out. So 165 * those indexes that didn't get allocated will have NULL in them. 166 * Break early on the first index that has NULL. 167 */ 168 if (get_serial_server()->frame_cap_recv_cspaths[i].capPtr == 0) { 169 break; 170 } 171 vka_cspace_free_path(parent_vka, get_serial_server()->frame_cap_recv_cspaths[i]); 172 } 173 } 174 free(get_serial_server()->frame_cap_recv_cspaths); 175 176 if (get_serial_server()->_badged_server_ep_cspath.capPtr != 0) { 177 vka_cspace_free_path(parent_vka, get_serial_server()->_badged_server_ep_cspath); 178 } 179 if (get_serial_server()->parent_badge_value != SERIAL_SERVER_BADGE_VALUE_EMPTY) { 180 serial_server_badge_value_free(get_serial_server()->parent_badge_value); 181 } 182 vka_free_object(parent_vka, &get_serial_server()->server_ep_obj); 183 return error; 184} 185 186int 187serial_server_parent_vka_mint_endpoint(vka_t *client_vka, 188 cspacepath_t *badged_server_ep_cspath) 189{ 190 if (client_vka == NULL || badged_server_ep_cspath == NULL) { 191 return seL4_InvalidArgument; 192 } 193 194 seL4_Word new_badge_value = serial_server_badge_value_alloc(); 195 if (new_badge_value == SERIAL_SERVER_BADGE_VALUE_EMPTY) { 196 return -1; 197 } 198 199 /* Mint the Endpoint to a new client. */ 200 return vka_mint_object_inter_cspace(get_serial_server()->server_vka, 201 &get_serial_server()->server_ep_obj, 202 client_vka, 203 badged_server_ep_cspath, 204 seL4_AllRights, 205 new_badge_value); 206} 207 208static inline void 209parent_ep_obj_to_cspath(cspacepath_t *result) 210{ 211 if (result == NULL) { 212 return; 213 } 214 vka_cspace_make_path(get_serial_server()->server_vka, 215 get_serial_server()->server_ep_obj.cptr, 216 result); 217} 218 219int 220serial_server_allocate_client_badged_ep(cspacepath_t dest_slot) 221{ 222 cspacepath_t server_ep_cspath; 223 224 seL4_Word new_badge_value = serial_server_badge_value_alloc(); 225 if (new_badge_value == SERIAL_SERVER_BADGE_VALUE_EMPTY) { 226 return -1; 227 } 228 229 parent_ep_obj_to_cspath(&server_ep_cspath); 230 return vka_cnode_mint(&dest_slot, &server_ep_cspath, seL4_AllRights, 231 new_badge_value); 232} 233 234seL4_CPtr 235serial_server_parent_mint_endpoint_to_process(sel4utils_process_t *p) 236{ 237 if (p == NULL) { 238 return 0; 239 } 240 241 cspacepath_t server_ep_cspath; 242 seL4_Word new_badge_value = serial_server_badge_value_alloc(); 243 if (new_badge_value == SERIAL_SERVER_BADGE_VALUE_EMPTY) { 244 return -1; 245 } 246 247 parent_ep_obj_to_cspath(&server_ep_cspath); 248 return sel4utils_mint_cap_to_process(p, server_ep_cspath, 249 seL4_AllRights, 250 new_badge_value); 251} 252