1 /* 2 * Resource server functionality. 3 * 4 * Copyright (C) 2018, 2019, 2020, 2021, 2022 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/re/env.h> 23 #include <l4/sys/types.h> 24 25 #include <pthread-l4.h> 26 #include <pthread.h> 27 28 #include "resource_server.h" 29 30 31 32 /* Convenience server methods. */ 33 34 /* Bind to a named IPC gate capability. */ 35 36 long ResourceServer::bind(const char *name) 37 { 38 return ipc_server_bind(name, (l4_umword_t) _config, &_config->server); 39 } 40 41 /* Start in the same thread indicating whether the server can be finalised. If 42 so, deletion notifications will be used. */ 43 44 long ResourceServer::start(bool finalisation) 45 { 46 resource_init_config(_config, _resource); 47 _config->thread = pthread_l4_cap(pthread_self()); 48 49 /* NOTE: On MIPS32, at least, in a payload started by libexec, the main thread 50 is not necessarily returned correctly. */ 51 52 if (l4_is_invalid_cap(_config->thread)) 53 _config->thread = l4re_env()->main_thread; 54 55 if (finalisation) 56 { 57 _config->finaliser = resource_same_thread_finaliser; 58 _config->notifications = 1; 59 _config->auto_deletion = 1; 60 } 61 62 return resource_start_config(_config, _resource); 63 } 64 65 /* Start serving a resource in an existing thread. The resource will not be 66 finalised. */ 67 68 long ResourceServer::start_in_thread(l4_cap_idx_t thread) 69 { 70 resource_init_config(_config, _resource); 71 resource_set_config_threaded(_config, thread, 1, 0, 0); 72 73 return resource_start_config(_config, _resource); 74 } 75 76 /* Start serving a resource in a new thread. */ 77 78 long ResourceServer::start_thread(bool finalisation, bool auto_deletion) 79 { 80 pthread_t thread; 81 pthread_attr_t attr; 82 long err; 83 84 pthread_attr_init(&attr); 85 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 86 87 resource_init_config(_config, _resource); 88 89 err = pthread_create(&thread, &attr, ipc_server_start_mainloop, _config); 90 if (err) 91 return err; 92 93 resource_set_config_threaded(_config, pthread_l4_cap(thread), 1, finalisation, 94 auto_deletion); 95 96 return resource_start_config(_config, _resource); 97 } 98 99 /* A convenience method starting a thread and returning the server capability 100 employed via the given parameter. */ 101 102 long ResourceServer::start_thread(l4_cap_idx_t *server, bool finalisation, 103 bool auto_deletion) 104 { 105 long err = start_thread(finalisation, auto_deletion); 106 107 if (!err) 108 *server = _config->server; 109 110 return err; 111 } 112 113 114 115 /* Initialise a server configuration for a resource. */ 116 117 void resource_init_config(ipc_server_config_type *config, Resource *resource) 118 { 119 ipc_server_default_config_type default_config = resource->config(); 120 121 config->handler_obj = resource->interface(); 122 config->finaliser_obj = resource; 123 config->expected_items = default_config.expected_items; 124 config->handler = default_config.handler; 125 } 126 127 /* Set a configuration to be threaded. */ 128 129 void resource_set_config_threaded(ipc_server_config_type *config, 130 l4_cap_idx_t thread, int separate_thread, 131 int finalisation, int auto_deletion) 132 { 133 /* Only invoke the finaliser where auto-deletion is employed. This assumes 134 that a single configuration is in use and thus a single resource that can 135 be managed in the server finalisation process. Where multiple 136 configurations are involved with an IPC gate, server finalisation does not 137 attempt to finalise all these configurations and associated resources, and 138 it therefore makes more sense to perform such finalisation elsewhere. */ 139 140 if (auto_deletion) 141 config->finaliser = resource_thread_finaliser; 142 143 config->config_thread = separate_thread; 144 config->thread = thread; 145 config->notifications = finalisation; 146 config->auto_deletion = auto_deletion; 147 } 148 149 /* Activate a resource and start a server for it. */ 150 151 long resource_start_config(ipc_server_config_type *config, Resource *resource) 152 { 153 resource->activate(); 154 long err = ipc_server_start_config(config); 155 156 /* Discard any server resources if starting it failed. */ 157 158 if (err) 159 { 160 ipc_server_finalise_config(config); 161 ipc_server_discard_thread(config); 162 } 163 164 return err; 165 } 166 167 168 169 /* A finaliser for exposed resources in the same thread. */ 170 171 void resource_same_thread_finaliser(ipc_server_config_type *config) 172 { 173 Resource *resource = reinterpret_cast<Resource *>(config->finaliser_obj); 174 175 /* Close but do not delete the resource since it is assumed that it is being 176 managed by the thread. */ 177 178 resource->close(); 179 180 /* Release the capabilities. */ 181 182 ipc_server_finalise_config(config); 183 delete config; 184 } 185 186 /* A finaliser for exposed resources in a different thread. */ 187 188 void resource_thread_finaliser(ipc_server_config_type *config) 189 { 190 Resource *resource = reinterpret_cast<Resource *>(config->finaliser_obj); 191 192 /* Close and delete the resource. */ 193 194 resource->close(); 195 delete resource; 196 197 /* Release the capabilities. */ 198 199 ipc_server_finalise_config(config); 200 delete config; 201 } 202 203 // vim: tabstop=2 expandtab shiftwidth=2