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 <autoconf.h> 13#include <sel4serialserver/gen_config.h> 14 15#include <stdio.h> 16#include <string.h> 17 18#include <sel4/sel4.h> 19#include <vka/vka.h> 20#include <vka/object.h> 21#include <vka/capops.h> 22 23#include <utils/arith.h> 24#include <utils/ansi.h> 25#include <sel4utils/api.h> 26#include <sel4utils/strerror.h> 27#include <sel4platsupport/platsupport.h> 28 29#include "serial_server.h" 30#include <serial_server/client.h> 31#include <serial_server/parent.h> 32 33/* Define global instance. */ 34static serial_server_context_t serial_server; 35 36static char *colors[] = { 37 ANSI_COLOR(RED), 38 ANSI_COLOR(GREEN), 39 ANSI_COLOR(YELLOW), 40 ANSI_COLOR(BLUE), 41 ANSI_COLOR(MAGENTA), 42 ANSI_COLOR(CYAN), 43 44 ANSI_COLOR(RED, BOLD), 45 ANSI_COLOR(GREEN, BOLD), 46 ANSI_COLOR(YELLOW, BOLD), 47 ANSI_COLOR(BLUE, BOLD), 48 ANSI_COLOR(MAGENTA, BOLD), 49 ANSI_COLOR(CYAN, BOLD), 50 51 ANSI_COLOR(RED, ITALIC), 52 ANSI_COLOR(GREEN, ITALIC), 53 ANSI_COLOR(YELLOW, ITALIC), 54 ANSI_COLOR(BLUE, ITALIC), 55 ANSI_COLOR(MAGENTA, ITALIC), 56 ANSI_COLOR(CYAN, ITALIC), 57 58 ANSI_COLOR(RED, UNDERLINE), 59 ANSI_COLOR(GREEN, UNDERLINE), 60 ANSI_COLOR(YELLOW, UNDERLINE), 61 ANSI_COLOR(BLUE, UNDERLINE), 62 ANSI_COLOR(MAGENTA, UNDERLINE), 63 ANSI_COLOR(CYAN, UNDERLINE), 64}; 65 66#define NUM_COLORS ARRAY_SIZE(colors) 67#define BADGE_TO_COLOR(badge) (colors[(badge) % NUM_COLORS]) 68 69serial_server_context_t *get_serial_server(void) 70{ 71 return &serial_server; 72} 73 74static inline seL4_MessageInfo_t recv(seL4_Word *sender_badge) 75{ 76 return api_recv(get_serial_server()->server_ep_obj.cptr, sender_badge, get_serial_server()->server_thread.reply.cptr); 77} 78 79static inline void reply(seL4_MessageInfo_t tag) 80{ 81 api_reply(get_serial_server()->server_thread.reply.cptr, tag); 82} 83 84serial_server_registry_entry_t *serial_server_registry_get_entry_by_badge(seL4_Word badge_value) 85{ 86 if (badge_value == SERIAL_SERVER_BADGE_VALUE_EMPTY 87 || get_serial_server()->registry == NULL 88 || badge_value > get_serial_server()->registry_n_entries) { 89 return NULL; 90 } 91 /* If the badge value has been released, return NULL. */ 92 if (get_serial_server()->registry[badge_value - 1].badge_value 93 == SERIAL_SERVER_BADGE_VALUE_EMPTY) { 94 return NULL; 95 } 96 97 return &get_serial_server()->registry[badge_value - 1]; 98} 99 100bool serial_server_badge_is_allocated(seL4_Word badge_value) 101{ 102 serial_server_registry_entry_t *tmp; 103 104 tmp = serial_server_registry_get_entry_by_badge(badge_value); 105 if (tmp == NULL) { 106 return false; 107 } 108 109 return tmp->badge_value != SERIAL_SERVER_BADGE_VALUE_EMPTY; 110} 111 112seL4_Word serial_server_badge_value_get_unused(void) 113{ 114 if (get_serial_server()->registry == NULL) { 115 return SERIAL_SERVER_BADGE_VALUE_EMPTY; 116 } 117 118 for (int i = 0; i < get_serial_server()->registry_n_entries; i++) { 119 if (get_serial_server()->registry[i].badge_value != SERIAL_SERVER_BADGE_VALUE_EMPTY) { 120 continue; 121 } 122 123 /* Badge value 0 will never be allocated, so index 0 is actually 124 * badge 1, and index 1 is badge 2, and so on ad infinitum. 125 */ 126 get_serial_server()->registry[i].badge_value = i + 1; 127 return get_serial_server()->registry[i].badge_value; 128 } 129 130 return SERIAL_SERVER_BADGE_VALUE_EMPTY; 131} 132 133seL4_Word serial_server_badge_value_alloc(void) 134{ 135 serial_server_registry_entry_t *tmp; 136 seL4_Word ret; 137 138 ret = serial_server_badge_value_get_unused(); 139 if (ret != SERIAL_SERVER_BADGE_VALUE_EMPTY) { 140 /* Success */ 141 return ret; 142 } 143 144 tmp = realloc(get_serial_server()->registry, 145 sizeof(*get_serial_server()->registry) 146 * (get_serial_server()->registry_n_entries + 1)); 147 if (tmp == NULL) { 148 ZF_LOGD(SERSERVS"badge_value_alloc: Failed resize pool."); 149 return SERIAL_SERVER_BADGE_VALUE_EMPTY; 150 } 151 152 get_serial_server()->registry = tmp; 153 get_serial_server()->registry[get_serial_server()->registry_n_entries].badge_value = 154 SERIAL_SERVER_BADGE_VALUE_EMPTY; 155 get_serial_server()->registry_n_entries++; 156 157 /* If it fails again (some other caller raced us and got the new ID before 158 * we did) that's tough luck -- the caller should probably look into 159 * serializing the calls. 160 */ 161 return serial_server_badge_value_get_unused(); 162} 163 164void serial_server_badge_value_free(seL4_Word badge_value) 165{ 166 serial_server_registry_entry_t *tmp; 167 168 if (badge_value == SERIAL_SERVER_BADGE_VALUE_EMPTY) { 169 return; 170 } 171 172 tmp = serial_server_registry_get_entry_by_badge(badge_value); 173 if (tmp == NULL) { 174 return; 175 } 176 177 tmp->badge_value = SERIAL_SERVER_BADGE_VALUE_EMPTY; 178} 179 180static void serial_server_registry_insert(seL4_Word badge_value, void *shmem, 181 seL4_CPtr *shmem_frame_caps, 182 size_t shmem_size) 183{ 184 serial_server_registry_entry_t *tmp; 185 186 tmp = serial_server_registry_get_entry_by_badge(badge_value); 187 /* If this is NULL, something went very wrong, because this function is only 188 * called after the server checks to ensure that the badge value has been 189 * allocated in the registry. Technically, this should never happen, but 190 * an assert doesn't hurt. 191 */ 192 assert(tmp != NULL); 193 194 tmp->badge_value = badge_value; 195 tmp->shmem = shmem; 196 tmp->shmem_size = shmem_size; 197 tmp->shmem_frame_caps = shmem_frame_caps; 198} 199 200static void serial_server_registry_remove(seL4_Word badge_value) 201{ 202 serial_server_registry_entry_t *tmp; 203 204 tmp = serial_server_registry_get_entry_by_badge(badge_value); 205 if (tmp == NULL) { 206 return; 207 } 208 serial_server_badge_value_free(badge_value); 209} 210 211static void serial_server_set_frame_recv_path(void) 212{ 213 seL4_SetCapReceivePath(get_serial_server()->server_cspace, 214 get_serial_server()->frame_cap_recv_cspaths[0].capPtr, 215 get_serial_server()->frame_cap_recv_cspaths[0].capDepth); 216} 217 218/** Processes all FUNC_CONNECT_REQ IPC messages. Establishes 219 * shared mem mappings with new clients and sets up book-keeping metadata. 220 * 221 * Clients calling connect() will pass us a list of Frame caps which we must 222 * map in order to establish shared mem with those clients. In this function, 223 * the library maps the client's frames into the server's VSpace. 224 */ 225seL4_Error serial_server_func_connect(seL4_MessageInfo_t tag, 226 seL4_Word client_badge_value, 227 size_t client_shmem_size) 228{ 229 seL4_Error error; 230 size_t client_shmem_n_pages; 231 void *shmem_tmp = NULL; 232 seL4_CPtr *client_frame_caps = NULL; 233 cspacepath_t client_frame_cspath_tmp; 234 235 if (client_shmem_size == 0) { 236 ZF_LOGW(SERSERVS"connect: Invalid shared mem window size of 0B.\n"); 237 return seL4_InvalidArgument; 238 } 239 240 client_shmem_n_pages = BYTES_TO_4K_PAGES(client_shmem_size); 241 /* The client should be allocated a badge value by the Parent, before it 242 * attempts to connect to the Server. 243 * 244 * The reason being that when the badge is allocated, the metadata array 245 * is resized as well, so badge allocation is also metadata allocation. 246 */ 247 if (client_badge_value == SERIAL_SERVER_BADGE_VALUE_EMPTY 248 || !serial_server_badge_is_allocated(client_badge_value)) { 249 ZF_LOGW(SERSERVS"connect: Please allocate a badge value to this new " 250 "client.\n"); 251 return -1; 252 } 253 /* Make sure that the client didn't request a shmem mapping larger than the 254 * server is willing to handle. 255 */ 256 if (client_shmem_n_pages > BYTES_TO_4K_PAGES(get_serial_server()->shmem_max_size)) { 257 /* If the client asks for a shmem mapping too large, we refuse, and 258 * send the value of shmem_max_size in SSMSGREG_RESPONSE 259 * so it can try again. 260 */ 261 ZF_LOGW(SERSERVS"connect: New client badge %x is asking to establish " 262 "shmem mapping of %dB, but server max accepted shmem size is " 263 "%dB.", 264 client_badge_value, client_shmem_size, 265 get_serial_server()->shmem_max_size); 266 return SERIAL_SERVER_ERROR_SHMEM_TOO_LARGE; 267 } 268 269 if (seL4_MessageInfo_get_extraCaps(tag) != client_shmem_n_pages) { 270 ZF_LOGW(SERSERVS"connect: Received %d Frame caps from client " 271 "badge %x.\n\tbut client requested shmem mapping of %d " 272 "frames. Possible cap transfer error.", 273 seL4_MessageInfo_get_extraCaps(tag), client_badge_value, 274 client_shmem_n_pages); 275 return seL4_InvalidCapability; 276 } 277 278 /* Prepare an array of the client's shmem Frame caps to be mapped into our 279 * VSpace. 280 */ 281 client_frame_caps = calloc((client_shmem_n_pages + 1), sizeof(seL4_CPtr)); 282 if (client_frame_caps == NULL) { 283 ZF_LOGE(SERSERVS"connect: Failed to alloc frame cap list for client " 284 "shmem."); 285 return seL4_NotEnoughMemory; 286 } 287 for (size_t i = 0; i < client_shmem_n_pages; i++) { 288 /* For each frame, we need to make an seL4_CNode_Copy of it before 289 * we zero-out the receive slots, or else when we delete the receive 290 * slots, the frames will be revoked and unmapped. 291 */ 292 error = vka_cspace_alloc_path(get_serial_server()->server_vka, 293 &client_frame_cspath_tmp); 294 if (error != 0) { 295 ZF_LOGE(SERSERVS"connect: Failed to alloc CSpace slot for frame " 296 "%zd of %zd received from client badge %lx.", 297 i + 1, client_shmem_n_pages, (long)client_badge_value); 298 goto out; 299 } 300 301 /* Copy the frame cap from the recv slot to the perm slot now. */ 302 error = vka_cnode_move(&client_frame_cspath_tmp, 303 &get_serial_server()->frame_cap_recv_cspaths[i]); 304 if (error != 0) { 305 ZF_LOGE(SERSERVS"connect: Failed to move %zuth frame-cap received " 306 " from client badge %lx.", i + 1, (long)client_badge_value); 307 goto out; 308 } 309 310 client_frame_caps[i] = client_frame_cspath_tmp.capPtr; 311 ZF_LOGD("connect: moved received client Frame cap %d from recv slot %"PRIxPTR" to slot %"PRIxPTR".", 312 i + 1, get_serial_server()->frame_cap_recv_cspaths[i].capPtr, 313 client_frame_caps[i]); 314 } 315 316 /* Map the frames into the vspace. */ 317 shmem_tmp = vspace_map_pages(get_serial_server()->server_vspace, client_frame_caps, 318 NULL, 319 seL4_AllRights, client_shmem_n_pages, 320 seL4_PageBits, 321 true); 322 if (shmem_tmp == NULL) { 323 ZF_LOGE(SERSERVS"connect: Failed to map shmem."); 324 error = seL4_NotEnoughMemory; 325 goto out; 326 } 327 328 serial_server_registry_insert(client_badge_value, shmem_tmp, 329 client_frame_caps, client_shmem_size); 330 331 ZF_LOGI(SERSERVS"connect: New client: badge %x, shmem %p, %d pages.", 332 client_badge_value, shmem_tmp, client_shmem_n_pages); 333 334 return seL4_NoError; 335 336out: 337 if (shmem_tmp != NULL) { 338 vspace_unmap_pages(get_serial_server()->server_vspace, shmem_tmp, 339 client_shmem_n_pages, seL4_PageBits, 340 VSPACE_PRESERVE); 341 } 342 343 if (client_frame_caps != NULL) { 344 for (size_t i = 0; i < client_shmem_n_pages; i++) { 345 /* Because client_frame_caps was alloc'd with calloc, we can depend on 346 * the unused slots being filled with 0s. Break on first unallocated. 347 */ 348 if (client_frame_caps[i] == 0) { 349 break; 350 } 351 client_frame_cspath_tmp.capPtr = client_frame_caps[i]; 352 vka_cspace_free_path(get_serial_server()->server_vka, 353 client_frame_cspath_tmp); 354 } 355 } 356 357 free(client_frame_caps); 358 return error; 359} 360 361static int serial_server_func_write(serial_server_registry_entry_t *client_data, 362 size_t message_len, size_t *bytes_written) 363{ 364 *bytes_written = 0; 365 366 if (client_data == NULL || bytes_written == NULL) { 367 ZF_LOGE(SERSERVS"printf: Got NULL for required argument."); 368 return seL4_InvalidArgument; 369 } 370 if (message_len > client_data->shmem_size) { 371 return seL4_RangeError; 372 } 373 374 /* Write out */ 375 if (config_set(CONFIG_SERIAL_SERVER_COLOURED_OUTPUT)) { 376 printf("%s", COLOR_RESET); 377 printf("%s", BADGE_TO_COLOR(client_data->badge_value)); 378 } 379 fwrite((void *)client_data->shmem, message_len, 1, stdout); 380 if (config_set(CONFIG_SERIAL_SERVER_COLOURED_OUTPUT)) { 381 printf("%s", COLOR_RESET); 382 } 383 *bytes_written = message_len; 384 return 0; 385} 386 387static void serial_server_func_disconnect(serial_server_registry_entry_t *client_data) 388{ 389 /* Tear down shmem and release the badge value for reuse. */ 390 vspace_unmap_pages(get_serial_server()->server_vspace, 391 (void *)client_data->shmem, 392 BYTES_TO_4K_PAGES(client_data->shmem_size), 393 seL4_PageBits, get_serial_server()->server_vka); 394 free(client_data->shmem_frame_caps); 395 serial_server_registry_remove(client_data->badge_value); 396} 397 398static void serial_server_func_kill(void) 399{ 400 /* Tear down all existing connections. */ 401 for (int i = 0; i < get_serial_server()->registry_n_entries; i++) { 402 serial_server_registry_entry_t *curr = &get_serial_server()->registry[i]; 403 404 if (curr->badge_value == SERIAL_SERVER_BADGE_VALUE_EMPTY 405 || BYTES_TO_4K_PAGES(curr->shmem_size) == 0) { 406 continue; 407 } 408 409 serial_server_func_disconnect(&get_serial_server()->registry[i]); 410 } 411} 412 413/** Debugging function -- prints out the state of the registry and the server's 414 * current list of clients. 415 * 416 * Can either print shallow or deep. 417 * @param dump_frame_caps If true, print the array of frames caps that underlie 418 * the shared mem mapping to each client. 419 */ 420void serial_server_registry_dump(bool dump_frame_caps) 421{ 422 serial_server_registry_entry_t *tmp; 423 424 if (get_serial_server()->registry == NULL) { 425 ZF_LOGD("Registry is NULL."); 426 } 427 for (int i = 0; i < get_serial_server()->registry_n_entries; i++) { 428 tmp = &get_serial_server()->registry[i]; 429 ZF_LOGD("Reg: idx %d, badge %d, shmem_size %d, shmem %p, caps %p.", 430 i, tmp->badge_value, tmp->shmem_size, tmp->shmem, 431 tmp->shmem_frame_caps); 432 433 if (!dump_frame_caps) { 434 continue; 435 } 436 437 for (int j = 0; j < BYTES_TO_4K_PAGES(tmp->shmem_size); j++) { 438 ZF_LOGD("Reg badge %d: frame cap %d: %"PRIxPTR".", 439 tmp->badge_value, j + 1, tmp->shmem_frame_caps[j]); 440 } 441 } 442} 443 444void serial_server_main(void) 445{ 446 seL4_MessageInfo_t tag; 447 seL4_Word sender_badge; 448 enum serial_server_funcs func; 449 int keep_going = 1; 450 UNUSED seL4_Error error; 451 serial_server_registry_entry_t *client_data = NULL; 452 size_t buff_len, bytes_written; 453 454 /* Bind to the serial driver. */ 455 error = platsupport_serial_setup_simple(get_serial_server()->server_vspace, 456 get_serial_server()->server_simple, 457 get_serial_server()->server_vka); 458 if (error != 0) { 459 ZF_LOGE(SERSERVS"main: Failed to bind to serial."); 460 } else { 461 ZF_LOGI(SERSERVS"main: Bound to the serial driver."); 462 } 463 464 /* The Parent will seL4_Call() the us, the Server, right after spawning us. 465 * It will expect us to seL4_Reply() with an error status code - we will 466 * send this Reply regardless of the outcome of the platform serial bind 467 * operation. 468 * 469 * First call seL4_Recv() to get the Reply cap back to the Parent, and then 470 * seL4_Reply to report our status. 471 */ 472 recv(&sender_badge); 473 474 seL4_SetMR(SSMSGREG_FUNC, FUNC_SERVER_SPAWN_SYNC_ACK); 475 tag = seL4_MessageInfo_new(error, 0, 0, SSMSGREG_SPAWN_SYNC_ACK_END); 476 reply(tag); 477 478 /* If the bind failed, this thread has essentially failed its mandate, so 479 * there is no reason to leave it scheduled. Kill it (to whatever extent 480 * that is possible). 481 */ 482 if (error != 0) { 483 seL4_TCB_Suspend(get_serial_server()->server_thread.tcb.cptr); 484 } 485 486 ZF_LOGI(SERSERVS"main: Entering main loop and accepting requests."); 487 while (keep_going) { 488 /* Set the CNode slots where caps from clients will go */ 489 serial_server_set_frame_recv_path(); 490 491 tag = recv(&sender_badge); 492 ZF_LOGD(SERSERVS "main: Got message from %x", sender_badge); 493 494 func = seL4_GetMR(SSMSGREG_FUNC); 495 496 /* Lookup the registry entry for this sender to make sure that the sender 497 * has a shmem buffer registered with us. If not, ignore the message. 498 * New connection requests are of course, exempt from the requirement to 499 * already have an established connection. 500 */ 501 if (func != FUNC_CONNECT_REQ) { 502 client_data = serial_server_registry_get_entry_by_badge(sender_badge); 503 if (client_data == NULL) { 504 ZF_LOGW(SERSERVS"main: Got message from unregistered client " 505 "badge %x. Ignoring.", 506 sender_badge); 507 continue; 508 } 509 } 510 511 switch (func) { 512 case FUNC_CONNECT_REQ: 513 ZF_LOGD(SERSERVS"main: Got connect request from client badge %x.", 514 sender_badge); 515 error = serial_server_func_connect(tag, 516 sender_badge, 517 seL4_GetMR(SSMSGREG_CONNECT_REQ_SHMEM_SIZE)); 518 519 seL4_SetMR(SSMSGREG_FUNC, FUNC_CONNECT_ACK); 520 seL4_SetMR(SSMSGREG_CONNECT_ACK_MAX_SHMEM_SIZE, 521 get_serial_server()->shmem_max_size); 522 tag = seL4_MessageInfo_new(error, 0, 0, SSMSGREG_CONNECT_ACK_END); 523 reply(tag); 524 break; 525 526 case FUNC_WRITE_REQ: 527 /* The Server's ABI for the write() request is as follows: 528 * 529 * The server returns the number of bytes it actually wrote out to the 530 * serial in a msg-reg (SSMSGREG_WRITE_ACK_N_BYTES_WRITTEN), 531 * aside from also returning an error code in the "label" of the header. 532 * 533 * This was done because attempting to overload the "label" of the 534 * header to hold a value that can be either: 535 * (1) a negative integer error code, (2) a positive size_t 536 * byte length 537 * Was not very clean especially since the bit-width of the "label" 538 * field shouldn't be assumed lightly, so separating the error code 539 * from the number of bytes written was the cleaner approach. 540 * 541 * At the client end, the client can decide to overload an ssize_t 542 * to encode both types of values. 543 */ 544 545 ZF_LOGD(SERSERVS"main: Got write request from client badge %x.", 546 sender_badge); 547 buff_len = seL4_GetMR(SSMSGREG_WRITE_REQ_BUFF_LEN); 548 error = serial_server_func_write(client_data, buff_len, 549 &bytes_written); 550 551 seL4_SetMR(SSMSGREG_FUNC, FUNC_WRITE_ACK); 552 seL4_SetMR(SSMSGREG_WRITE_ACK_N_BYTES_WRITTEN, bytes_written); 553 tag = seL4_MessageInfo_new(error, 0, 0, SSMSGREG_WRITE_ACK_END); 554 reply(tag); 555 break; 556 557 case FUNC_DISCONNECT_REQ: 558 ZF_LOGD(SERSERVS"main: Got disconnect request from client badge %x.", 559 sender_badge); 560 serial_server_func_disconnect(client_data); 561 562 seL4_SetMR(SSMSGREG_FUNC, FUNC_DISCONNECT_ACK); 563 tag = seL4_MessageInfo_new(error, 0, 0, SSMSGREG_DISCONNECT_ACK_END); 564 reply(tag); 565 break; 566 567 case FUNC_KILL_REQ: 568 ZF_LOGI(SERSERVS"main: Got KILL request from client badge %x.", 569 sender_badge); 570 /* The actual contents of the Reply don't matter here. */ 571 seL4_SetMR(SSMSGREG_FUNC, FUNC_KILL_ACK); 572 tag = seL4_MessageInfo_new(0, 0, 0, SSMSGREG_KILL_ACK_END); 573 reply(tag); 574 /* Break out of the loop */ 575 keep_going = 0; 576 break; 577 578 default: 579 ZF_LOGW(SERSERVS "main: Unknown function %d requested.", func); 580 break; 581 } 582 } 583 584 serial_server_func_kill(); 585 /* After we break out of the loop, seL4_TCB_Suspend ourselves */ 586 ZF_LOGI(SERSERVS"main: Suspending."); 587 seL4_TCB_Suspend(get_serial_server()->server_thread.tcb.cptr); 588} 589