From 3c1945fea858e4dceba5efaaaf0d554746d83224 Mon Sep 17 00:00:00 2001 From: Chris Guikema Date: Thu, 27 Oct 2022 16:28:46 -0400 Subject: [PATCH 1/3] SerialServer: allow clients to be non-sequential This commit adds a helper function to get a client structure based on the badge. The allows for clients to be numerical, but not sequential, and guarantees serial input will always align to the correct client on each build. A side-effect of this method is that the clients are switched to by typing the ESCAPE_CHAR + badge number. This limits the number of clients to single-digits, 0 -> 9. Signed-off-by: Chris Guikema --- components/SerialServer/src/serial.c | 50 ++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/components/SerialServer/src/serial.c b/components/SerialServer/src/serial.c index 0854e66..7c0d276 100644 --- a/components/SerialServer/src/serial.c +++ b/components/SerialServer/src/serial.c @@ -24,7 +24,8 @@ #include "server_virtqueue.h" #define ESCAPE_CHAR '@' -#define MAX_CLIENTS 12 +#define MAX_CLIENTS 10 +#define MAX_BADGE (MAX_CLIENTS-1) #define CLIENT_OUTPUT_BUFFER_SIZE 4096 #define CTRL_C 0x03 @@ -73,13 +74,11 @@ const char *all_output_colours[2][MAX_CLIENTS] = { { /* Processed streams */ ANSI_COLOR(RED), - ANSI_COLOR(GREEN), ANSI_COLOR(BLUE), ANSI_COLOR(MAGENTA), ANSI_COLOR(YELLOW), ANSI_COLOR(CYAN), ANSI_COLOR(RED, BOLD) - ANSI_COLOR(GREEN, BOLD), ANSI_COLOR(BLUE, BOLD), ANSI_COLOR(MAGENTA, BOLD), ANSI_COLOR(YELLOW, BOLD), @@ -88,13 +87,11 @@ const char *all_output_colours[2][MAX_CLIENTS] = { { /* Raw streams */ ANSI_COLOR2(RED, WHITE), - ANSI_COLOR2(GREEN, WHITE), ANSI_COLOR2(BLUE, WHITE), ANSI_COLOR2(MAGENTA, WHITE), ANSI_COLOR2(YELLOW, WHITE), ANSI_COLOR2(CYAN, WHITE), ANSI_COLOR2(RED, WHITE, BOLD) - ANSI_COLOR2(GREEN, WHITE, BOLD), ANSI_COLOR2(BLUE, WHITE, BOLD), ANSI_COLOR2(MAGENTA, WHITE, BOLD), ANSI_COLOR2(YELLOW, WHITE, BOLD), @@ -176,7 +173,7 @@ static int is_newline(const uint8_t *c) return (c[0] == '\r' && c[1] == '\n') || (c[0] == '\n' && c[1] == '\r'); } -static int active_client = 0; +static int active_client = -2; static int active_multiclients = 0; /* Try coalescing client output. This is intended for use with @@ -313,9 +310,23 @@ static void internal_putchar(int b, int c) error = serial_unlock(); } +static getchar_client_t *getchar_client_from_badge(int badge) +{ + for (int i = 0; i < num_getchar_clients; i++) { + if (getchar_clients[i].client_id == badge) { + return &getchar_clients[i]; + } + } + return NULL; +} + static void internal_raw_putchar(int id, int c) { - getchar_client_t *client = &getchar_clients[id]; + getchar_client_t *client = getchar_client_from_badge(id); + if (NULL == client) { + ZF_LOGW("Client with badge %d not found", id); + return; + } uint32_t next_tail = (client->buf->tail + 1) % sizeof(client->buf->buf); if (next_tail == client->buf->head) { /* full */ @@ -406,7 +417,7 @@ static void handle_char(uint8_t c) last_out = -1; printf(COLOR_RESET "\r\n --- SerialServer help ---" "\r\n Escape char: %c" - "\r\n 0 - %-2d switches input to that client" + "\r\n # - switches to client #" "\r\n ? shows this help" "\r\n s dump seL4 scheduler (depends on CONFIG_DEBUG_BUILD)" "\r\n m simultaneous multi-client input" @@ -414,16 +425,20 @@ static void handle_char(uint8_t c) "\r\n 0: no debugging" "\r\n 1: debug multi-input mode output coalescing" "\r\n 2: debug flush_buffer_line" - "\r\n", ESCAPE_CHAR, num_getchar_clients - 1); + "\r\n", ESCAPE_CHAR); statemachine = 1; break; default: last_out = -1; statemachine = 1; - if (c >= '0' && c < '0' + num_getchar_clients) { - int client = c - '0'; - printf(COLOR_RESET "\r\nSwitching input to %d\r\n", client); - active_client = client; + if (c >= '0' && c <= '9') { + int badge = c - '0'; + if (NULL == getchar_client_from_badge(badge)) { + printf(COLOR_RESET "\r\nClient %d is not valid\r\n", badge); + } else { + printf(COLOR_RESET "\r\nSwitching input to %d\r\n", badge); + active_client = badge; + } } else { printf(COLOR_RESET "\r\nInvalid SerialServer command: %c" "\r\nType %c? for help" @@ -523,10 +538,17 @@ void pre_init(void) getchar_clients = calloc(num_getchar_clients, sizeof(getchar_client_t)); for (int i = 0; i < num_getchar_clients; i++) { unsigned int badge = getchar_enumerate_badge(i); - assert(badge <= num_getchar_clients); + if (badge > MAX_BADGE) { + ZF_LOGE("Client badge %d exceeds range 0->%d", badge, MAX_BADGE); + continue; + } getchar_clients[i].client_id = badge; getchar_clients[i].buf = getchar_buf(badge); getchar_clients[i].last_head = -1; + + if (-2 == active_client) { + active_client = badge; + } } } plat_post_init(&(io_ops.irq_ops)); From f177e8d831060793cb4972dedf7527dbef116abc Mon Sep 17 00:00:00 2001 From: Chris Guikema Date: Wed, 2 Nov 2022 23:35:15 -0400 Subject: [PATCH 2/3] SerialServer: initialize putchar interfaces A zero badge is technically an unbadged notification, which would allow the owner to distribute the capability. CAmkES makes sure the attributes aren't set to zero, but that meant that "Client 0" was unused. This commit initializes the putchar interfaces, and subtracts 1 from the badge to get a client ID. This adds a runtime check to ensure a badge of 0 cannot be used by the serial server. Signed-off-by: Chris Guikema --- components/SerialServer/src/serial.c | 162 ++++++++++++++++++++++++--- 1 file changed, 148 insertions(+), 14 deletions(-) diff --git a/components/SerialServer/src/serial.c b/components/SerialServer/src/serial.c index 7c0d276..94b3da9 100644 --- a/components/SerialServer/src/serial.c +++ b/components/SerialServer/src/serial.c @@ -25,21 +25,41 @@ #define ESCAPE_CHAR '@' #define MAX_CLIENTS 10 -#define MAX_BADGE (MAX_CLIENTS-1) +#define MAX_BADGE MAX_CLIENTS #define CLIENT_OUTPUT_BUFFER_SIZE 4096 #define CTRL_C 0x03 #define CTRL_Z 0x1A +enum putchar_interfaces { + RAW_PUTCHAR, + PROCESSED_PUTCHAR, + RAW_BATCH, + PROCESSED_BATCH, + NUM_PUTCHAR_INTERFACES, +}; + /* TODO: have the MultiSharedData template generate a header with these */ void getchar_emit(unsigned int id) WEAK; seL4_Word getchar_enumerate_badge(unsigned int id) WEAK; +seL4_Word raw_putchar_enumerate_badge(unsigned int id) WEAK; +seL4_Word raw_batch_enumerate_badge(unsigned int id) WEAK; +seL4_Word processed_putchar_enumerate_badge(unsigned int id) WEAK; +seL4_Word processed_batch_enumerate_badge(unsigned int id) WEAK; unsigned int getchar_num_badges() WEAK; +unsigned int raw_putchar_num_badges() WEAK; +unsigned int raw_batch_num_badges() WEAK; +unsigned int processed_putchar_num_badges() WEAK; +unsigned int processed_batch_num_badges() WEAK; void *getchar_buf(unsigned int id) WEAK; void *processed_batch_buf(unsigned int id) WEAK; void *raw_batch_buf(unsigned int id) WEAK; int getchar_largest_badge(void) WEAK; +typedef seL4_Word(*enumerate_badge_t)(unsigned int); +typedef unsigned int (*num_badges_t)(void); +typedef void *(*get_buf_t)(seL4_Word); + typedef struct getchar_buffer { uint32_t head; uint32_t tail; @@ -54,6 +74,18 @@ typedef struct getchar_client { uint32_t last_head; } getchar_client_t; +typedef struct putchar_client { + unsigned int client_id; + client_buffer_t *buf; +} putchar_client_t; + +typedef struct putchar_info { + enumerate_badge_t enumerate_badge; + num_badges_t num_badges; + get_buf_t get_buf; + const char *name; +} putchar_info_t; + static ps_io_ops_t io_ops; static int last_out = -1; @@ -69,6 +101,36 @@ static int has_data = 0; static int num_getchar_clients = 0; static getchar_client_t *getchar_clients = NULL; +static putchar_client_t **putchar_clients; +static int num_putchar_clients[NUM_PUTCHAR_INTERFACES]; + +static putchar_info_t putchar_information[NUM_PUTCHAR_INTERFACES] = { + { + .num_badges = raw_putchar_num_badges, + .enumerate_badge = raw_putchar_enumerate_badge, + .get_buf = NULL, + .name = "raw_putchar", + }, + { + .num_badges = processed_putchar_num_badges, + .enumerate_badge = processed_putchar_enumerate_badge, + .get_buf = NULL, + .name = "processed_putchar", + }, + { + .num_badges = raw_batch_num_badges, + .enumerate_badge = raw_batch_enumerate_badge, + .get_buf = raw_batch_buf, + .name = "raw_batch", + }, + { + .num_badges = processed_batch_num_badges, + .enumerate_badge = processed_batch_enumerate_badge, + .get_buf = processed_batch_buf, + .name = "processed_batch", + }, +}; + /* We predefine output colours for clients */ const char *all_output_colours[2][MAX_CLIENTS] = { { @@ -320,6 +382,23 @@ static getchar_client_t *getchar_client_from_badge(int badge) return NULL; } +static putchar_client_t *putchar_client_from_badge(int badge, int interface) +{ + if (interface >= NUM_PUTCHAR_INTERFACES) { + ZF_LOGW("Interface number %d is invalid", interface); + return NULL; + } + putchar_client_t *putchar_intf_clients = putchar_clients[interface]; + int num_clients = num_putchar_clients[interface]; + + for (int i = 0; i < num_clients; i++) { + if (putchar_intf_clients[i].client_id == badge) { + return &putchar_intf_clients[i]; + } + } + return NULL; +} + static void internal_raw_putchar(int id, int c) { getchar_client_t *client = getchar_client_from_badge(id); @@ -432,11 +511,12 @@ static void handle_char(uint8_t c) last_out = -1; statemachine = 1; if (c >= '0' && c <= '9') { - int badge = c - '0'; + int client = c - '0'; + int badge = client + 1; if (NULL == getchar_client_from_badge(badge)) { - printf(COLOR_RESET "\r\nClient %d is not valid\r\n", badge); + printf(COLOR_RESET "\r\nClient %d is not valid\r\n", client); } else { - printf(COLOR_RESET "\r\nSwitching input to %d\r\n", badge); + printf(COLOR_RESET "\r\nSwitching input to %d\r\n", client); active_client = badge; } } else { @@ -538,8 +618,10 @@ void pre_init(void) getchar_clients = calloc(num_getchar_clients, sizeof(getchar_client_t)); for (int i = 0; i < num_getchar_clients; i++) { unsigned int badge = getchar_enumerate_badge(i); - if (badge > MAX_BADGE) { - ZF_LOGE("Client badge %d exceeds range 0->%d", badge, MAX_BADGE); + if (badge > MAX_BADGE || !badge) { + ZF_LOGE("getchar badge %d invalid. " + "Set client's interface_attributes to a value " + "between 1 and %d", badge, MAX_BADGE); continue; } getchar_clients[i].client_id = badge; @@ -551,6 +633,32 @@ void pre_init(void) } } } + + putchar_clients = calloc(NUM_PUTCHAR_INTERFACES, sizeof(putchar_client_t *)); + ZF_LOGF_IF(NULL == putchar_clients, "Failed to calloc putchar clients"); + + for (int i = 0; i < NUM_PUTCHAR_INTERFACES; i++) { + putchar_info_t putchar_info = putchar_information[i]; + if (putchar_info.num_badges) { + num_putchar_clients[i] = putchar_info.num_badges(); + putchar_clients[i] = calloc(num_putchar_clients[i], sizeof(putchar_client_t)); + ZF_LOGF_IF(NULL == putchar_clients[i], "Failed to calloc putchar client entry"); + for (int j = 0; j < num_putchar_clients[i]; j++) { + unsigned int badge = putchar_info.enumerate_badge(j); + if (badge > MAX_BADGE || !badge) { + ZF_LOGE("%s badge %d invalid. " + "Set client's interface_attributes to a value " + "between 1 and %d", putchar_info.name, badge, MAX_BADGE); + continue; + } + putchar_clients[i][j].client_id = badge; + if (putchar_info.get_buf) { + putchar_clients[i][j].buf = putchar_info.get_buf(badge); + } + } + } + } + plat_post_init(&(io_ops.irq_ops)); /* Start regular heartbeat of 500ms */ timeout_periodic(0, 500000000); @@ -571,30 +679,49 @@ seL4_Word processed_putchar_get_sender_id(void) WEAK; void processed_putchar_putchar(int c) { seL4_Word n = processed_putchar_get_sender_id(); + putchar_client_t *client = putchar_client_from_badge(n, PROCESSED_PUTCHAR); + if (NULL == client) { + ZF_LOGW("Client with badge %d not found", n); + return; + } + if (c == '\n') { - internal_putchar(n, '\r'); + internal_putchar(client->client_id - 1, '\r'); } - internal_putchar((int)n, c); + internal_putchar((int)client->client_id - 1, c); } seL4_Word raw_putchar_get_sender_id(void) WEAK; void raw_putchar_putchar(int c) { seL4_Word n = raw_putchar_get_sender_id(); - internal_putchar((int)n + MAX_CLIENTS, c); + putchar_client_t *client = putchar_client_from_badge(n, RAW_PUTCHAR); + if (NULL == client) { + ZF_LOGW("Client with badge %d not found", n); + return; + } + + internal_putchar((int)client->client_id - 1 + MAX_CLIENTS, c); } seL4_Word processed_batch_get_sender_id(void) WEAK; void processed_batch_batch(void) { seL4_Word n = processed_batch_get_sender_id(); - client_buffer_t *putchar_buf = processed_batch_buf(n); + putchar_client_t *client = putchar_client_from_badge(n, PROCESSED_BATCH); + if (NULL == client) { + ZF_LOGW("Client with badge %d not found", n); + return; + } + + client_buffer_t *putchar_buf = client->buf; + while (putchar_buf->head != putchar_buf->tail) { char ch = putchar_buf->buf[putchar_buf->head]; if (ch == '\n') { - internal_putchar(n, '\r'); + internal_putchar(client->client_id - 1, '\r'); } - internal_putchar((int)n, ch); + internal_putchar((int)client->client_id - 1, ch); putchar_buf->head = (putchar_buf->head + 1) % sizeof(putchar_buf->buf); } } @@ -603,10 +730,17 @@ seL4_Word raw_batch_get_sender_id(void) WEAK; void raw_batch_batch(void) { seL4_Word n = raw_batch_get_sender_id(); - client_buffer_t *putchar_buf = raw_batch_buf(n); + putchar_client_t *client = putchar_client_from_badge(n, RAW_BATCH); + if (NULL == client) { + ZF_LOGW("Client with badge %d not found", n); + return; + } + + client_buffer_t *putchar_buf = client->buf; + while (putchar_buf->head != putchar_buf->tail) { char ch = putchar_buf->buf[putchar_buf->head]; - internal_putchar((int)n + MAX_CLIENTS, ch); + internal_putchar((int)client->client_id - 1 + MAX_CLIENTS, ch); putchar_buf->head = (putchar_buf->head + 1) % sizeof(putchar_buf->buf); } } From 4353bdfd817b35e2c6ad7d7e7bb568aa5c3c61c4 Mon Sep 17 00:00:00 2001 From: Chris Guikema Date: Thu, 3 Nov 2022 13:45:50 -0400 Subject: [PATCH 3/3] SerialServer: generate function headers This commit does two things. One, it uses CAmkES to automatically generate the function headers. Two, it adds a template with a WEAK version of the headers, since if an interface isn't connected, the headers will not generate. Signed-off-by: Chris Guikema --- components/SerialServer/CMakeLists.txt | 4 ++++ components/SerialServer/src/serial.c | 17 --------------- .../templates/seL4SerialServer.template.h | 21 +++++++++++++++++++ global-connectors.cmake | 2 ++ 4 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 components/SerialServer/templates/seL4SerialServer.template.h diff --git a/components/SerialServer/CMakeLists.txt b/components/SerialServer/CMakeLists.txt index 44e94dc..46d858c 100644 --- a/components/SerialServer/CMakeLists.txt +++ b/components/SerialServer/CMakeLists.txt @@ -27,6 +27,8 @@ else() set(PlatPrefix "${KernelPlatform}") endif() +CAmkESAddTemplatesPath(templates) + DeclareCAmkESComponent( SerialServer SOURCES @@ -37,6 +39,8 @@ DeclareCAmkESComponent( LIBS virtqueue vswitch + TEMPLATE_HEADERS + seL4SerialServer.template.h ) CAmkESAddCPPInclude("${CMAKE_CURRENT_LIST_DIR}/include/plat/${PlatPrefix}/") diff --git a/components/SerialServer/src/serial.c b/components/SerialServer/src/serial.c index 94b3da9..3175494 100644 --- a/components/SerialServer/src/serial.c +++ b/components/SerialServer/src/serial.c @@ -39,23 +39,6 @@ enum putchar_interfaces { NUM_PUTCHAR_INTERFACES, }; -/* TODO: have the MultiSharedData template generate a header with these */ -void getchar_emit(unsigned int id) WEAK; -seL4_Word getchar_enumerate_badge(unsigned int id) WEAK; -seL4_Word raw_putchar_enumerate_badge(unsigned int id) WEAK; -seL4_Word raw_batch_enumerate_badge(unsigned int id) WEAK; -seL4_Word processed_putchar_enumerate_badge(unsigned int id) WEAK; -seL4_Word processed_batch_enumerate_badge(unsigned int id) WEAK; -unsigned int getchar_num_badges() WEAK; -unsigned int raw_putchar_num_badges() WEAK; -unsigned int raw_batch_num_badges() WEAK; -unsigned int processed_putchar_num_badges() WEAK; -unsigned int processed_batch_num_badges() WEAK; -void *getchar_buf(unsigned int id) WEAK; -void *processed_batch_buf(unsigned int id) WEAK; -void *raw_batch_buf(unsigned int id) WEAK; -int getchar_largest_badge(void) WEAK; - typedef seL4_Word(*enumerate_badge_t)(unsigned int); typedef unsigned int (*num_badges_t)(void); typedef void *(*get_buf_t)(seL4_Word); diff --git a/components/SerialServer/templates/seL4SerialServer.template.h b/components/SerialServer/templates/seL4SerialServer.template.h new file mode 100644 index 0000000..86e847e --- /dev/null +++ b/components/SerialServer/templates/seL4SerialServer.template.h @@ -0,0 +1,21 @@ +/* + * Copyright 2022, DornerWorks + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +void getchar_emit(unsigned int id) WEAK; +seL4_Word getchar_enumerate_badge(unsigned int id) WEAK; +seL4_Word raw_putchar_enumerate_badge(unsigned int id) WEAK; +seL4_Word raw_batch_enumerate_badge(unsigned int id) WEAK; +seL4_Word processed_putchar_enumerate_badge(unsigned int id) WEAK; +seL4_Word processed_batch_enumerate_badge(unsigned int id) WEAK; +unsigned int getchar_num_badges() WEAK; +unsigned int raw_putchar_num_badges() WEAK; +unsigned int raw_batch_num_badges() WEAK; +unsigned int processed_putchar_num_badges() WEAK; +unsigned int processed_batch_num_badges() WEAK; +void *getchar_buf(seL4_Word client_id) WEAK; +void *processed_batch_buf(seL4_Word client_id) WEAK; +void *raw_batch_buf(seL4_Word client_id) WEAK; +int getchar_largest_badge(void) WEAK; diff --git a/global-connectors.cmake b/global-connectors.cmake index e3af9dc..2fb21af 100644 --- a/global-connectors.cmake +++ b/global-connectors.cmake @@ -84,6 +84,8 @@ DeclareCAmkESConnector( seL4RPCDataportSignal-from.template.h TO seL4RPCDataportSignal-to.template.c + TO_HEADER + seL4RPCDataportSignal-to.template.h ) DeclareCAmkESConnector(