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