| <!--- | 
 |   Copyright 2017, Data61 | 
 |   Commonwealth Scientific and Industrial Research Organisation (CSIRO) | 
 |   ABN 41 687 119 230. | 
 |  | 
 |   This software may be distributed and modified according to the terms of | 
 |   the BSD 2-Clause license. Note that NO WARRANTY is provided. | 
 |   See "LICENSE_BSD2.txt" for details. | 
 |  | 
 |     @TAG(DATA61_BSD) | 
 | --> | 
 | # 1. MISSION STATEMENT AND CURRENT STATUS. | 
 |  | 
 | The Serial Server thread is able to connect to a character device and act as a | 
 | multiplexer for writes to that device. | 
 |  | 
 | Generally the server's error messages are very descriptive, and you should be | 
 | able to tell what went wrong if you triggered an error. | 
 |  | 
 | ## 1.1. CURRENT SUPPORTED FEATURES: | 
 | * Binding to a platform serial device. | 
 | * Writing to the platform serial device. | 
 | * Serializing access to the serial device from multiple clients. | 
 |  | 
 | ## 1.2. CURRENTLY UNSUPPORTED FEATURES: | 
 | * Reading from the platform serial device. | 
 | * Multiple server instances. | 
 | * Write-back buffering or any kind of cache-type buffering really. | 
 |  | 
 | # 2. TOP LEVEL DESIGN | 
 |  | 
 | > **Caution**: | 
 | > | 
 | > All `vka_t`, `vspace_t` and `simple_t` instances provided to this library | 
 | > must remain functional for the duration of the Server thread's lifetime. | 
 |  | 
 | ## 2.1. ROLES. | 
 |  | 
 | The library works on the basis of 3 roles being played by 3 parties: | 
 |  | 
 | * The "Parent", which spawns the server thread by calling | 
 | `serial_server_parent_spawn_thread()`. It is also generally, though not necessarily, | 
 | going to be the job of the Parent, to provide CSpace slots for the | 
 | Server to mint badged caps to its Endpoint into, but this may be done by any other | 
 | thread that exists in the same VSpace as the Server (because the Server's | 
 | badge-value allocation metadata exists in the Server's VSpace). | 
 | * The "Server", which is spawned by the Parent, and multiplexes accesses to the | 
 | underlying serial device. The Server also provides a badge value allocator that | 
 | the Parent *must* use when it is minting the Endpoint capabilities to the Clients. | 
 | * The "Clients", which connect to the Server and print through it. The Clients | 
 | need to have an endpoint minted to them by the Parent, or else they need to | 
 | somehow acquire a badged capability to the Server's Endpoint, the badge of which | 
 | was generated by the library. | 
 |  | 
 | Finally, some party must spawn the client threads themselves; this party is, | 
 | while a recognized actor, not a necessary part of the library specification. | 
 |  | 
 | ## 2.2. ROLE QUICK REFERENCE (TL;DR): | 
 |  | 
 | Basic quick walkthrough of what you need to do, in order to get each role | 
 | working. Written role-specific so you only need to read the section you care | 
 | about. | 
 |  | 
 | ### 2.2.1. SERVER QUICK REFERENCE: | 
 |  | 
 | You don't need to do anything with the server. If you want to know how to | 
 | _spawn_ the server, that's the role of the Parent. | 
 |  | 
 | ### 2.2.2. PARENT QUICK REFERENCE: | 
 |  | 
 | Header: `#include <sel4utils/serial_server/parent.h>`. | 
 |  | 
 | Of the three roles, the Parent does the most work, and is responsible for: | 
 |  | 
 | * Spawning the Server. | 
 | * Generally, but not necessarily: allocating CSpace slots in the clients' CSpaces | 
 |   into which the Server library will mint badged Endpoint capabilities. | 
 |  | 
 | To spawn the Server, you'll need: | 
 |  | 
 | * A `simple_t` instance that has information about the Parent. | 
 | * A `vka_t` instance that manages the Parent's CSpace. | 
 | * A `vspace_t` instance that manages the Parent's VSpace. | 
 |  | 
 | When you have all of those, go ahead and spawn the Server thread like this: | 
 |     int error; | 
 |  | 
 |     error = serial_server_parent_spawn_thread(...); | 
 |     if (error != 0) { | 
 |         ZF_LOGF("Failed to spawn the server with error %d.", error); | 
 |     } | 
 |     ZF_LOGF("Serial server spawned."); | 
 |  | 
 | > #### Behaviour / Side effects | 
 | > | 
 | > * The server works with the assumption that it will be spawned in the Parent's | 
 | >   VSpace and CSpace, and it expects to be given the Parent's vspace_t, vka_t | 
 | >   and simple_t. | 
 | > * When the Server thread is spawned, the library automatically creates an | 
 | >   Endpoint object that it will listen on, and stores it a capability to this | 
 | >   Endpoint internally. | 
 | > * Keep all vka_t, vspace_t and simple_t instances that are passed to this | 
 | >   library alive for the duration of the lifetime of the Server thread. | 
 |  | 
 | Next you'll want to mint badged capabilities to the Server's Endpoint object, | 
 | to all the Clients you intend to spawn. The library takes care of this in a | 
 | two-fold manner: | 
 |  | 
 | * It provides convenience functions for minting new, badged copies of the Server | 
 | thread's Endpoint cap. | 
 | * It keeps an internal badge value allocator, which the aforementioned | 
 | convenience functions use to generate unique badge values for clients. | 
 |  | 
 | The library provides several convenience functions that will help you mint these easily: | 
 |  | 
 | * `serial_server_parent_vka_mint_endpoint(vka_t client_vka, cspacepath_t *result)`: | 
 | This function will ALLOCATE and mint a new, badged copy of the Server's Endpoint and return | 
 | the path into for it in `result`. Make sure you pass the **CLIENT**'s vka, and | 
 | not the Server's vka, since you're trying to mint the new cap to the client. | 
 | * `serial_server_allocate_client_badged_ep(cspacepath_t dest_path)`: | 
 | This function will mint a new, badged copy of the Server's Endpoint into the | 
 | designated slot -- this is meant to be used when the caller has already pre-allocated | 
 | a slot that s/he would like the server to use. | 
 | * `serial_server_parent_mint_endpoint_to_process(sel4utils_process_t *p)`: | 
 | This function relies on `sel4utils_mint_cap_to_process()`, but basically fills out | 
 | the arguments for you. By implication, it also takes up a slot in the destination | 
 | process's CSpace, so **if you had some policy for your client threads' slots, | 
 | you need to factor this function call into that policy**. | 
 |  | 
 | In this quick-reference, we'll use `serial_server_parent_vka_mint_endpoint()` | 
 | since it's super-convenient. We will be using 3 Client threads in our example: | 
 |  | 
 |     #define SERSERV_README_N_CLIENTS (3) | 
 |  | 
 |     vka_t client_vkas[SERSERV_README_N_CLIENTS]; | 
 |     cspacepath_t client_server_ep_cspaths[SERSERV_README_N_CLIENTS]; | 
 |  | 
 |     for (int i = 0; i < SERSERV_README_N_CLIENTS; i++) { | 
 |         /* I'm not going to cover how to initialize vkas here. */ | 
 |         SETUP_YOUR_CLIENT'S_VKA(&client_vkas[i]); | 
 |  | 
 |         /* Ask the Serrver to Mint badged endpoints to the clients: the library | 
 |          * automatically both allocates a unique badge value and mints the | 
 |          * capability for us. | 
 |          * | 
 |          * We chose here to use `serial_server_parent_vka_mint_endpoint()`, but | 
 |          * the other functions provided by the library would have worked fine. | 
 |          */ | 
 |         error = serial_server_parent_vka_mint_endpoint(&client_vkas[i], | 
 |                                                        &client_server_ep_cspaths[i]); | 
 |     } | 
 |  | 
 |     /* At the end of this loop, each Client has a badged capability to the | 
 |      * Endpoint that the server is listening on. (The client processes/threads | 
 |      * have not been spawned yet.) | 
 |      * | 
 |      * You want to keep the CPtrs to the badged endpoints somewhere safe, | 
 |      * because you'll need them later. | 
 |      */ | 
 |  | 
 | Make sure that you save the CPtrs to the badged Endpoints that the library | 
 | returned to you: you'll need them later on. If your new Client will live in | 
 | a new VSpace or CSpace, **make sure you plan for how to make the these caps | 
 | visible to them**. | 
 |  | 
 | Finally, either the Parent or some other actor will have to actually spawn the | 
 | clients. I won't be covering how to spawn TCBs/CSpaces/VSpaces, etc here, but | 
 | it'll look something like: | 
 |  | 
 |     for (int i = 0; i < SERSERV_README_N_CLIENTS; i++) { | 
 |         MAKE_SERVER's_BADGED_ENDPOINT_CAPABILITY_VISIBLE_TO_CLIENT(i); | 
 |  | 
 |         SPAWN_CLIENT_THREAD_OR_PROCESS(i); | 
 |     } | 
 |  | 
 | ### 2.2.3. CLIENT QUICK REFERENCE: | 
 |  | 
 | Header: `#include <sel4utils/serial_server/client.h>`. | 
 |  | 
 | The Client role is extremely simple: you retrieve the badged Endpoint capability | 
 | that was minted for you by the Parent, and then you call | 
 | `serial_server_client_connect()` to connect to the server. From then on, you can | 
 | call `serial_server_printf()` to ask the server to print. | 
 |  | 
 | A call to `serial_server_client_connect()` requires: | 
 |  | 
 | * The client's badged Server endpoint capability. | 
 | * An initialized vka_t instance which is able to allocate resources on behalf | 
 |   of the client. If the client lives in the same CSpace as the Parent, this will | 
 |   be the Parent's VKA. | 
 | * An initialize vspace_t instance which is able to allocate memory on behalf of | 
 |   the client. If the client lives in the same VSpace as the Parent, this will | 
 |   be the Parent's VSpace. | 
 | * An uninitialized `serial_client_context_t`, which is your context-cookie | 
 |   to the server. | 
 |  | 
 | You can use the library from a client like this: | 
 |  | 
 |     void client_main(void) | 
 |     { | 
 |         seL4_CPtr my_server_ep_cap; | 
 |         vka_t *my_vka; | 
 |         vspace_t *my_vspace; | 
 |         serial_client_context_t my_conn; | 
 |         int error; | 
 |  | 
 |         RETRIEVE_MY_BADGED_EP_CAP(&my_server_ep_cap); | 
 |  | 
 |         my_vka = OBTAIN_A_VKA_THAT_WORKS_FOR_MY_CSPACE(); | 
 |         my_vspace = OBTAIN_A_VSPACE_THAT_WORKS_FOR_MY_VSPACE(); | 
 |  | 
 |         error = serial_server_client_connect(my_server_ep_cap, | 
 |                                              my_vka, my_vspace, | 
 |                                              &my_conn); | 
 |         if (error != 0) { | 
 |             ZF_LOGF("Failed to connect to the server."); | 
 |         } | 
 |  | 
 |         /* From here on, you're free to print. */ | 
 |         serial_server_printf(&my_conn, "Hello world from %s.\n", "John Doe"); | 
 |     } | 
 |  | 
 | > #### Behaviour / Side effects | 
 | > | 
 | > * `serial_server_client_connect()` establishes a shared-memory window | 
 | > between the client and server. Make sure that you have enough virtual | 
 | > memory in both VSpaces, and make sure you have enough physical memory. | 
 | > At present, the shared mem window is 1 page in size. | 
 | > * `serial_server_client_connect()` also sends capabilities to the server via | 
 | > IPC. Be sure that the badged Endpoint capabilities generated for each | 
 | > client have the **GRANT** right on them. | 
 |  | 
 | # 3. HIGH LEVEL SERIAL SERVER MECHANICS: | 
 |  | 
 | ## 3.1 DISCONNECTING: | 
 |  | 
 | Disconnecting causes the Server to tear down its connection to a specific | 
 | Client. That client will thenceforth be ignored by the Server, just as if it had | 
 | never connected in the first place. The client may subsequently reconnect if it | 
 | so desires. | 
 |  | 
 | > #### Behaviour / Side effects: | 
 | > | 
 | > After being disconnected, the only operation that a former Client can invoke | 
 | > on the Server is the `serial_server_client_connect()` function. | 
 |  | 
 | ### 3.1.1: DISCONNECTING, SERVER ROLE AND PARENT ROLE: | 
 |  | 
 | You don't need to do anything with the Server or Parent when you wish to | 
 | disconnect a Client from the Server. | 
 |  | 
 | ### 3.1.2: DISCONNECTING, CLIENT ROLE: | 
 |  | 
 | To disconnect a client from the Server, simply retrieve a pointer to your | 
 | connection token, and then call `serial_server_disconnect()`. (Your connection | 
 | token was filled out and returned to you when you called | 
 | `serial_server_client_connect()`). | 
 |  | 
 |     void client_main(void) | 
 |     { | 
 |         serial_client_context_t conn; | 
 |  | 
 |         serial_server_client_connect(&conn, ...); | 
 |  | 
 |         /* ... */ | 
 |  | 
 |         serial_server_disconnect(&conn); | 
 |     } | 
 |  | 
 | That's it. | 
 |  | 
 | ## 3.2 KILLING THE SERVER: | 
 |  | 
 | Killing the server causes the Server thread to first stop listening for | 
 | requests (it exits its message loop), and then unilaterally tear down all | 
 | connections to its current clients, before Suspending itself. | 
 |  | 
 | This operation may only be performed by a current Client of the Server. If your | 
 | Parent wishes to kill the Server, it itself will have to also connect to the | 
 | Server. | 
 |  | 
 | ### 3.2.1 KILLING, SERVER ROLE AND PARENT ROLE: | 
 |  | 
 | You don't need to do anything with the Server or Parent when you wish to kill the | 
 | Server. If you wish to kill the Server from the Parent, then the Parent will | 
 | also itself need to connect to the Server. | 
 |  | 
 | ### 3.2.2 KILLING, CLIENT ROLE: | 
 |  | 
 | To kill the Server, simply obtain a handle to your connection token, and then | 
 | call `serial_server_kill()`: | 
 |  | 
 |     void client_main(void) | 
 |     { | 
 |         serial_client_context_t conn; | 
 |  | 
 |         serial_server_client_connect(&conn); | 
 |  | 
 |         /* ... */ | 
 |  | 
 |         serial_server_kill(&conn); | 
 |     } |