1 /* 2 * Resource server functionality. 3 * 4 * Copyright (C) 2018-2022, 2024 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 _config->server = l4re_env_get_cap(name); 39 return L4_EOK; 40 } 41 42 /* Start in the same thread indicating whether the server can be finalised. If 43 so, deletion notifications will be used. */ 44 45 long ResourceServer::start(bool finalisation) 46 { 47 resource_init_config(_config, _resource); 48 _config->thread = pthread_l4_cap(pthread_self()); 49 50 /* NOTE: On MIPS32, at least, in a payload started by libexec, the main thread 51 is not necessarily returned correctly. */ 52 53 if (l4_is_invalid_cap(_config->thread)) 54 _config->thread = l4re_env()->main_thread; 55 56 if (finalisation) 57 { 58 _config->finaliser = resource_same_thread_finaliser; 59 _config->notifications = 1; 60 _config->auto_deletion = 1; 61 } 62 63 return resource_start_config(_config, _resource); 64 } 65 66 /* Start serving a resource in an existing thread. The resource will not be 67 finalised. */ 68 69 long ResourceServer::start_in_thread(l4_cap_idx_t thread) 70 { 71 resource_init_config(_config, _resource); 72 resource_set_config_threaded(_config, thread, 1, 0, 0); 73 74 return resource_start_config(_config, _resource); 75 } 76 77 /* Start serving a resource in a new thread. */ 78 79 long ResourceServer::start_thread(bool finalisation, bool auto_deletion) 80 { 81 pthread_t thread; 82 pthread_attr_t attr; 83 long err; 84 85 pthread_attr_init(&attr); 86 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 87 88 resource_init_config(_config, _resource); 89 90 err = pthread_create(&thread, &attr, ipc_server_start_mainloop, _config); 91 if (err) 92 return err; 93 94 resource_set_config_threaded(_config, pthread_l4_cap(thread), 1, finalisation, 95 auto_deletion); 96 97 return resource_start_config(_config, _resource); 98 } 99 100 /* A convenience method starting a thread and returning the server capability 101 employed via the given parameter. */ 102 103 long ResourceServer::start_thread(l4_cap_idx_t *server, bool finalisation, 104 bool auto_deletion) 105 { 106 long err = start_thread(finalisation, auto_deletion); 107 108 if (!err) 109 *server = _config->server; 110 111 return err; 112 } 113 114 115 116 /* Initialise a server configuration for a resource. */ 117 118 void resource_init_config(ipc_server_config_type *config, Resource *resource) 119 { 120 ipc_server_default_config_type default_config = resource->config(); 121 122 config->handler_obj = resource->interface(); 123 config->finaliser_obj = resource; 124 config->expected_items = default_config.expected_items; 125 config->handler = default_config.handler; 126 } 127 128 /* Set a configuration to be threaded. */ 129 130 void resource_set_config_threaded(ipc_server_config_type *config, 131 l4_cap_idx_t thread, int separate_thread, 132 int finalisation, int auto_deletion) 133 { 134 /* Only invoke the finaliser where auto-deletion is employed. This assumes 135 that a single configuration is in use and thus a single resource that can 136 be managed in the server finalisation process. Where multiple 137 configurations are involved with an IPC gate, server finalisation does not 138 attempt to finalise all these configurations and associated resources, and 139 it therefore makes more sense to perform such finalisation elsewhere. */ 140 141 if (auto_deletion) 142 config->finaliser = resource_thread_finaliser; 143 144 config->config_thread = separate_thread; 145 config->thread = thread; 146 config->notifications = finalisation; 147 config->auto_deletion = auto_deletion; 148 } 149 150 /* Activate a resource and start a server for it. */ 151 152 long resource_start_config(ipc_server_config_type *config, Resource *resource) 153 { 154 resource->activate(); 155 long err = ipc_server_start_config(config); 156 157 /* Discard any server resources if starting it failed. */ 158 159 if (err) 160 { 161 ipc_server_finalise_config(config); 162 ipc_server_discard_thread(config); 163 } 164 165 return err; 166 } 167 168 169 170 /* A finaliser for exposed resources in the same thread. */ 171 172 void resource_same_thread_finaliser(ipc_server_config_type *config) 173 { 174 Resource *resource = reinterpret_cast<Resource *>(config->finaliser_obj); 175 176 /* Close but do not delete the resource since it is assumed that it is being 177 managed by the thread. */ 178 179 resource->close(); 180 181 /* Release the capabilities. */ 182 183 ipc_server_finalise_config(config); 184 delete config; 185 } 186 187 /* A finaliser for exposed resources in a different thread. */ 188 189 void resource_thread_finaliser(ipc_server_config_type *config) 190 { 191 Resource *resource = reinterpret_cast<Resource *>(config->finaliser_obj); 192 193 /* Close and delete the resource. */ 194 195 resource->close(); 196 delete resource; 197 198 /* Release the capabilities. */ 199 200 ipc_server_finalise_config(config); 201 delete config; 202 } 203 204 // vim: tabstop=2 expandtab shiftwidth=2