1 /* 2 * Server binding/registration. 3 * 4 * Copyright (C) 2018, 2019, 2020, 2021, 2022, 5 * 2023 Paul Boddie <paul@boddie.org.uk> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA 21 */ 22 23 #include <l4/re/env.h> 24 #include <l4/sys/factory.h> 25 #include <l4/sys/kernel_object.h> 26 #include <l4/sys/rcv_endpoint.h> 27 #include <l4/sys/thread.h> 28 #include <l4/util/util.h> 29 30 #include <stdlib.h> 31 32 #include "cap_alloc.h" 33 #include "irq.h" 34 #include "server.h" 35 36 37 38 /* Associate an IRQ with the IPC gate in the main thread to handle gate deletion 39 notifications. */ 40 41 long ipc_server_apply_irq(l4_cap_idx_t cap, l4_cap_idx_t *irq, l4_umword_t id) 42 { 43 return ipc_server_apply_irq_for_thread(cap, irq, id, l4re_env()->main_thread); 44 } 45 46 /* Associate an IRQ with the IPC gate in the given thread to handle gate deletion 47 notifications. */ 48 49 long ipc_server_apply_irq_for_thread(l4_cap_idx_t cap, l4_cap_idx_t *irq, 50 l4_umword_t id, l4_cap_idx_t thread) 51 { 52 /* Create an IRQ for the gate. */ 53 54 long err = ipc_create_irq(irq); 55 56 if (err) 57 return err; 58 59 /* Bind the IRQ to the thread, presenting the label if it provides the 60 incoming message. */ 61 62 err = ipc_bind_irq(*irq, id, thread); 63 64 if (err) 65 { 66 ipc_cap_free_um(*irq); 67 return err; 68 } 69 70 /* Register for notifications. The thread associated with the gate seems to be 71 needed. */ 72 73 err = l4_error(l4_thread_register_del_irq(thread, *irq)); 74 75 if (err) 76 { 77 ipc_cap_free_um(*irq); 78 return err; 79 } 80 81 /* Decrease the reference count on the server capability (IPC gate). */ 82 83 return l4_error(l4_kobject_dec_refcnt(cap, 1)); 84 } 85 86 87 88 /* Bind the main thread to the named capability, employing an identifier for 89 the IPC gate and returning the capability index. */ 90 91 long ipc_server_bind(const char *name, l4_umword_t id, l4_cap_idx_t *server) 92 { 93 /* Obtain a reference to the device. */ 94 95 *server = l4re_env_get_cap(name); 96 97 if (l4_is_invalid_cap(*server)) 98 return -L4_ENOENT; 99 100 /* Bind to an IPC gate with the label identifying the server as message 101 destination. */ 102 103 return l4_error(l4_rcv_ep_bind_thread(*server, l4re_env()->main_thread, id)); 104 } 105 106 /* Initialise a new server capability, binding it to the main thread. */ 107 108 long ipc_server_new(l4_cap_idx_t *cap, void *obj) 109 { 110 return ipc_server_new_for_thread(cap, obj, l4re_env()->main_thread); 111 } 112 113 /* Initialise a new server capability, binding it to the given thread. */ 114 115 long ipc_server_new_for_thread(l4_cap_idx_t *cap, void *obj, l4_cap_idx_t thread) 116 { 117 l4_umword_t label = (l4_umword_t) obj; 118 119 /* Create an IPC gate, presenting the resource's label if this gate provides 120 the message. */ 121 122 return ipc_server_new_gate_for_thread(cap, thread, label); 123 } 124 125 /* Create an IPC gate, exporting via a new capability an object with the given 126 identifier. */ 127 128 long ipc_server_new_gate(l4_cap_idx_t *ref, l4_umword_t id) 129 { 130 return ipc_server_new_gate_for_thread(ref, l4re_env()->main_thread, id); 131 } 132 133 /* Create an IPC gate associated with a specific thread. */ 134 135 long ipc_server_new_gate_for_thread(l4_cap_idx_t *ref, l4_cap_idx_t thread, l4_umword_t id) 136 { 137 long err; 138 139 *ref = ipc_cap_alloc(); 140 141 if (l4_is_invalid_cap(*ref)) 142 return -L4_ENOENT; 143 144 /* Associate the object with an IPC gate. */ 145 146 err = l4_error(l4_factory_create_gate(l4re_env()->factory, *ref, thread, id)); 147 148 if (err) 149 ipc_cap_free_um(*ref); 150 151 return err; 152 } 153 154 155 156 /* Support for the convenience macro starting a server for a given object. */ 157 158 long _ipc_server_loop_for(ipc_server_default_config_type *default_config, 159 void *handler_obj, 160 const char *name) 161 { 162 ipc_server_config_type config; 163 long err; 164 165 if (name != NULL) 166 { 167 err = ipc_server_bind(name, (l4_umword_t) &config.endpoint, &config.server); 168 if (err) 169 return err; 170 } 171 172 _ipc_server_init_for(&config, default_config, handler_obj); 173 174 return ipc_server_start_config(&config); 175 } 176 177 /* Initialise a server for a given object. */ 178 179 long _ipc_server_init_for(ipc_server_config_type *config, 180 ipc_server_default_config_type *default_config, 181 void *handler_obj) 182 { 183 ipc_server_init_config(config); 184 185 config->expected_items = default_config->expected_items; 186 config->handler = default_config->handler; 187 config->handler_obj = handler_obj; 188 189 return L4_EOK; 190 } 191 192 /* Associate a new configuration with an existing server endpoint. */ 193 194 long _ipc_server_add_config(ipc_server_config_type *config, 195 ipc_server_default_config_type *default_config, 196 void *handler_obj, 197 l4_cap_idx_t thread) 198 { 199 _ipc_server_init_for(config, default_config, handler_obj); 200 return ipc_server_start_config_thread(config, thread); 201 } 202 203 /* Complete initialisation of a server in the given thread. */ 204 205 long ipc_server_start_config_thread(ipc_server_config_type *config, 206 l4_cap_idx_t thread) 207 { 208 config->thread = thread; 209 config->config_thread = 1; 210 211 return ipc_server_prepare_config(config); 212 } 213 214 215 216 /* A server main loop handling endpoint deletion for IPC gates. The supplied 217 configuration object indicates common properties for components exposed via 218 IPC gates whose messages are handled by this loop. Where an IRQ is received 219 for the principal IPC gate, as indicated by the supplied configuration, the 220 loop terminates. */ 221 222 long ipc_server_managed_loop(ipc_server_config_type *config) 223 { 224 ipc_message_t msg; 225 l4_umword_t label; 226 227 /* Permit other endpoints by dynamically interpreting the label. */ 228 229 ipc_server_endpoint_type *endpoint; 230 231 /* Declare the extent to which capabilities are expected in messages. */ 232 233 long err = ipc_message_expect(&msg, config->expected_items); 234 235 if (err) 236 return err; 237 238 /* Wait for an incoming message. */ 239 240 while (1) 241 { 242 ipc_message_wait(&msg, &label); 243 244 /* Clear lower label bits. */ 245 246 label = label & ~3UL; 247 248 /* Ignore erroneous messages. */ 249 250 if (l4_ipc_error(msg.tag, l4_utcb())) 251 continue; 252 253 /* Obtain the endpoint and determine whether it represents an IRQ or a 254 component. */ 255 256 endpoint = (ipc_server_endpoint_type *) label; 257 258 /* Message involves the IRQ. */ 259 260 if (l4_is_valid_cap(endpoint->irq)) 261 { 262 /* Finalise the associated configuration. */ 263 264 if (endpoint->config->finaliser != NULL) 265 endpoint->config->finaliser(config); 266 267 /* Terminate if the principal configuration is involved. */ 268 269 if (endpoint->config == config) 270 break; 271 } 272 273 /* Message involves a component. */ 274 275 else 276 { 277 endpoint->config->handler(&msg, endpoint->config->handler_obj); 278 } 279 280 /* A termination condition occurred. */ 281 282 if (msg.terminating) 283 break; 284 } 285 286 /* Free expected capabilities. */ 287 288 ipc_message_free(&msg); 289 290 return L4_EOK; 291 } 292 293 /* A pthread-compatible mainloop initiation function. */ 294 295 void *ipc_server_start_mainloop(void *data) 296 { 297 ipc_server_config_type *config = (ipc_server_config_type *) data; 298 299 return (void *) ipc_server_managed_loop(config); 300 } 301 302 /* Wait for an incoming message via an IPC gate dedicated to a particular 303 object within its own thread. */ 304 305 l4_msgtag_t ipc_server_wait(l4_umword_t id) 306 { 307 l4_umword_t label; 308 l4_msgtag_t tag; 309 310 /* Ignore errors and messages not intended for the identified gate. */ 311 312 do tag = l4_ipc_wait(l4_utcb(), &label, L4_IPC_NEVER); 313 while (l4_ipc_error(tag, l4_utcb()) || (label & ~3UL) != id); 314 315 return tag; 316 } 317 318 319 320 /* Finalise a server configuration. */ 321 322 void ipc_server_finalise_config(ipc_server_config_type *config) 323 { 324 if (l4_is_valid_cap(config->server)) 325 { 326 ipc_cap_free_um(config->server); 327 config->server = L4_INVALID_CAP; 328 } 329 330 if (l4_is_valid_cap(config->irq_endpoint.irq)) 331 { 332 ipc_cap_free_um(config->irq_endpoint.irq); 333 config->irq_endpoint.irq = L4_INVALID_CAP; 334 } 335 } 336 337 /* Discard any uninitiated thread. */ 338 339 void ipc_server_discard_thread(ipc_server_config_type *config) 340 { 341 if (config->config_thread && l4_is_valid_cap(config->thread)) 342 { 343 ipc_cap_free_um(config->thread); 344 config->thread = L4_INVALID_CAP; 345 } 346 } 347 348 /* Initialise a server configuration. */ 349 350 void ipc_server_init_config(ipc_server_config_type *config) 351 { 352 /* No object references defined. */ 353 354 config->handler_obj = NULL; 355 config->finaliser_obj = NULL; 356 357 /* Expect no capability items by default. */ 358 359 config->expected_items = 0; 360 361 /* No handler or finaliser defined. */ 362 363 config->handler = NULL; 364 config->finaliser = NULL; 365 366 /* No separate thread for the main loop. */ 367 368 config->config_thread = 0; 369 370 /* Main thread by default with IPC gate to be allocated. */ 371 372 config->thread = l4re_env()->main_thread; 373 config->server = L4_INVALID_CAP; 374 375 /* Define the general endpoint with invalid IRQ capability. */ 376 377 config->endpoint.irq = L4_INVALID_CAP; 378 config->endpoint.config = config; 379 380 /* Disable notifications, with the IRQ and associated configuration 381 potentially defined later. */ 382 383 config->irq_endpoint.irq = L4_INVALID_CAP; 384 config->irq_endpoint.config = NULL; 385 } 386 387 /* Initialise but do not start a server using the given configuration. */ 388 389 long ipc_server_prepare_config(ipc_server_config_type *config) 390 { 391 long err; 392 393 /* Allocate a new IPC gate if one is not already defined. */ 394 395 if (l4_is_invalid_cap(config->server)) 396 { 397 err = ipc_server_new_for_thread(&config->server, &config->endpoint, config->thread); 398 399 if (err) 400 return err; 401 } 402 403 /* Allocate an IRQ for notifications if requested. */ 404 405 if (config->irq_endpoint.config != NULL) 406 { 407 err = ipc_server_apply_irq_for_thread(config->server, &config->irq_endpoint.irq, 408 (l4_umword_t) &config->irq_endpoint, config->thread); 409 410 if (err) 411 return err; 412 413 /* Unmask the interrupt. */ 414 415 ipc_init_irq(config->irq_endpoint.irq); 416 } 417 418 return L4_EOK; 419 } 420 421 /* Initialise and start a server using the given configuration. */ 422 423 long ipc_server_start_config(ipc_server_config_type *config) 424 { 425 long err = ipc_server_prepare_config(config); 426 427 if (err) 428 return err; 429 430 /* With a separate thread, return the last status value. Otherwise, invoke the 431 main loop. */ 432 433 if (config->config_thread) 434 return L4_EOK; 435 else 436 return (long) ipc_server_start_mainloop(config); 437 } 438 439 /* vim: tabstop=2 expandtab shiftwidth=2 440 */