1 /* 2 * Server binding/registration. 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/factory.h> 24 #include <l4/sys/kernel_object.h> 25 #include <l4/sys/rcv_endpoint.h> 26 #include <l4/sys/thread.h> 27 #include <l4/util/util.h> 28 29 #include <stdlib.h> 30 31 #include "cap_alloc.h" 32 #include "irq.h" 33 #include "server.h" 34 35 36 37 /* Associate an IRQ with the IPC gate in the main thread to handle gate deletion 38 notifications. */ 39 40 long ipc_server_apply_irq(l4_cap_idx_t cap, l4_cap_idx_t *irq) 41 { 42 return ipc_server_apply_irq_for_thread(cap, irq, l4re_env()->main_thread); 43 } 44 45 /* Associate an IRQ with the IPC gate in the given thread to handle gate deletion 46 notifications. */ 47 48 long ipc_server_apply_irq_for_thread(l4_cap_idx_t cap, l4_cap_idx_t *irq, l4_cap_idx_t thread) 49 { 50 /* Create an IRQ for the gate. */ 51 52 long err = ipc_create_irq(irq); 53 54 if (err) 55 return err; 56 57 /* Bind the IRQ to the thread, presenting the label if it provides the 58 incoming message. */ 59 60 err = l4_error(l4_rcv_ep_bind_thread(*irq, thread, (l4_umword_t) *irq)); 61 62 if (err) 63 { 64 ipc_cap_free_um(*irq); 65 return err; 66 } 67 68 /* Register for notifications. The thread associated with the gate seems to be 69 needed. */ 70 71 err = l4_error(l4_thread_register_del_irq(thread, *irq)); 72 73 if (err) 74 { 75 ipc_cap_free_um(*irq); 76 return err; 77 } 78 79 /* Decrease the reference count on the server capability (IPC gate). */ 80 81 return l4_error(l4_kobject_dec_refcnt(cap, 1)); 82 } 83 84 85 86 /* Bind the main thread to the named capability, employing an identifier for 87 the IPC gate and returning the capability index. */ 88 89 long ipc_server_bind(const char *name, l4_umword_t id, l4_cap_idx_t *server) 90 { 91 /* Obtain a reference to the device. */ 92 93 *server = l4re_env_get_cap(name); 94 95 if (l4_is_invalid_cap(*server)) 96 return -L4_ENOENT; 97 98 /* Bind to an IPC gate with the label identifying the server as message 99 destination. */ 100 101 return l4_error(l4_rcv_ep_bind_thread(*server, l4re_env()->main_thread, id)); 102 } 103 104 /* Initialise a new server capability, binding it to the main thread. */ 105 106 long ipc_server_new(l4_cap_idx_t *cap, void *obj) 107 { 108 return ipc_server_new_for_thread(cap, obj, l4re_env()->main_thread); 109 } 110 111 /* Initialise a new server capability, binding it to the given thread. */ 112 113 long ipc_server_new_for_thread(l4_cap_idx_t *cap, void *obj, l4_cap_idx_t thread) 114 { 115 l4_umword_t label = (l4_umword_t) obj; 116 117 /* Create an IPC gate, presenting the resource's label if this gate provides 118 the message. */ 119 120 return ipc_server_new_gate_for_thread(cap, thread, label); 121 } 122 123 /* Create an IPC gate, exporting via a new capability an object with the given 124 identifier. */ 125 126 long ipc_server_new_gate(l4_cap_idx_t *ref, l4_umword_t id) 127 { 128 return ipc_server_new_gate_for_thread(ref, l4re_env()->main_thread, id); 129 } 130 131 /* Create an IPC gate associated with a specific thread. */ 132 133 long ipc_server_new_gate_for_thread(l4_cap_idx_t *ref, l4_cap_idx_t thread, l4_umword_t id) 134 { 135 long err; 136 137 *ref = ipc_cap_alloc(); 138 139 if (l4_is_invalid_cap(*ref)) 140 return -L4_ENOENT; 141 142 /* Associate the object with an IPC gate. */ 143 144 err = l4_error(l4_factory_create_gate(l4re_env()->factory, *ref, thread, id)); 145 146 if (err) 147 ipc_cap_free_um(*ref); 148 149 return err; 150 } 151 152 153 154 /* Support for the convenience macro starting a server for a given object. */ 155 156 long _ipc_server_loop_for(ipc_server_default_config_type *default_config, 157 void *handler_obj, 158 const char *name) 159 { 160 ipc_server_config_type config; 161 long err; 162 163 if (name != NULL) 164 { 165 err = ipc_server_bind(name, (l4_umword_t) &config, &config.server); 166 if (err) 167 return err; 168 } 169 170 _ipc_server_init_for(&config, default_config, handler_obj); 171 172 return ipc_server_start_config(&config); 173 } 174 175 /* Initialise a server for a given object. */ 176 177 long _ipc_server_init_for(ipc_server_config_type *config, 178 ipc_server_default_config_type *default_config, 179 void *handler_obj) 180 { 181 ipc_server_init_config(config); 182 183 config->expected_items = default_config->expected_items; 184 config->handler = default_config->handler; 185 config->handler_obj = handler_obj; 186 187 return L4_EOK; 188 } 189 190 /* Associate a new configuration with an existing server endpoint. */ 191 192 long _ipc_server_add_config(ipc_server_config_type *config, 193 ipc_server_default_config_type *default_config, 194 void *handler_obj, 195 l4_cap_idx_t thread) 196 { 197 _ipc_server_init_for(config, default_config, handler_obj); 198 return ipc_server_start_config_thread(config, thread); 199 } 200 201 /* Complete initialisation of a server in the given thread. */ 202 203 long ipc_server_start_config_thread(ipc_server_config_type *config, 204 l4_cap_idx_t thread) 205 { 206 config->thread = thread; 207 config->config_thread = 1; 208 209 return ipc_server_start_config(config); 210 } 211 212 213 214 /* A server main loop handling endpoint deletion for an IPC gate dedicated to 215 a particular object within its own thread. */ 216 217 long ipc_server_managed_loop(ipc_server_config_type *config) 218 { 219 ipc_message_t msg; 220 l4_umword_t label, irq_label; 221 222 /* Permit other endpoints by dynamically interpreting the label. */ 223 224 ipc_server_config_type *config_from_label; 225 226 /* Declare the extent to which capabilities are expected in messages. */ 227 228 long err = ipc_message_expect(&msg, config->expected_items); 229 230 if (err) 231 return err; 232 233 /* Wait for an incoming message. */ 234 235 while (1) 236 { 237 ipc_message_wait(&msg, &label); 238 239 /* Clear lower label bits. */ 240 241 label = label & ~3UL; 242 243 /* Ignore erroneous messages. */ 244 245 if (l4_ipc_error(msg.tag, l4_utcb())) 246 continue; 247 248 /* Message involves the IPC gate itself. */ 249 250 irq_label = (l4_umword_t) config->irq; 251 252 if (!config->notifications || (config->notifications && (label != irq_label))) 253 { 254 config_from_label = (ipc_server_config_type *) label; 255 config_from_label->handler(&msg, config_from_label->handler_obj); 256 } 257 258 /* Message involves the IRQ or a termination condition occurred. */ 259 260 else if ((config->notifications && (label == irq_label)) || msg.terminating) 261 break; 262 } 263 264 /* Free expected capabilities. */ 265 266 ipc_message_free(&msg); 267 268 return L4_EOK; 269 } 270 271 /* A pthread-compatible mainloop initiation function. */ 272 273 void *ipc_server_start_mainloop(void *data) 274 { 275 ipc_server_config_type *config = (ipc_server_config_type *) data; 276 277 long err = ipc_server_managed_loop(config); 278 279 if (config->finaliser != NULL) 280 config->finaliser(config); 281 282 return (void *) err; 283 } 284 285 /* Wait for an incoming message via an IPC gate dedicated to a particular 286 object within its own thread. */ 287 288 l4_msgtag_t ipc_server_wait(l4_umword_t id) 289 { 290 l4_umword_t label; 291 l4_msgtag_t tag; 292 293 /* Ignore errors and messages not intended for the identified gate. */ 294 295 do tag = l4_ipc_wait(l4_utcb(), &label, L4_IPC_NEVER); 296 while (l4_ipc_error(tag, l4_utcb()) || (label & ~3UL) != id); 297 298 return tag; 299 } 300 301 302 303 /* Finalise a server configuration. */ 304 305 void ipc_server_finalise_config(ipc_server_config_type *config) 306 { 307 if (l4_is_valid_cap(config->server)) 308 { 309 ipc_cap_free_um(config->server); 310 config->server = L4_INVALID_CAP; 311 } 312 313 if (l4_is_valid_cap(config->irq)) 314 { 315 ipc_cap_free_um(config->irq); 316 config->irq = L4_INVALID_CAP; 317 } 318 } 319 320 /* Discard any uninitiated thread. */ 321 322 void ipc_server_discard_thread(ipc_server_config_type *config) 323 { 324 if (config->config_thread && l4_is_valid_cap(config->thread)) 325 { 326 ipc_cap_free_um(config->thread); 327 config->thread = L4_INVALID_CAP; 328 } 329 } 330 331 /* Initialise a server configuration. */ 332 333 void ipc_server_init_config(ipc_server_config_type *config) 334 { 335 /* No object references defined. */ 336 337 config->handler_obj = NULL; 338 config->finaliser_obj = NULL; 339 340 /* Expect no capability items by default. */ 341 342 config->expected_items = 0; 343 344 /* No handler or finaliser defined. */ 345 346 config->handler = NULL; 347 config->finaliser = NULL; 348 349 /* No separate thread for the main loop. */ 350 351 config->config_thread = 0; 352 353 /* Main thread by default with IPC gate to be allocated. */ 354 355 config->thread = l4re_env()->main_thread; 356 config->server = L4_INVALID_CAP; 357 358 /* No notifications and with IRQ to be potentially allocated. */ 359 360 config->notifications = 0; 361 config->irq = L4_INVALID_CAP; 362 } 363 364 /* Initialise and start a server using the given configuration. */ 365 366 long ipc_server_start_config(ipc_server_config_type *config) 367 { 368 long err; 369 370 /* Allocate a new IPC gate if one is not already defined. */ 371 372 if (l4_is_invalid_cap(config->server)) 373 { 374 err = ipc_server_new_for_thread(&config->server, config, config->thread); 375 376 if (err) 377 return err; 378 } 379 380 /* Allocate an IRQ for notifications if requested. */ 381 382 if (config->notifications) 383 { 384 err = ipc_server_apply_irq_for_thread(config->server, &config->irq, config->thread); 385 386 if (err) 387 return err; 388 389 /* Unmask the interrupt. */ 390 391 ipc_init_irq(config->irq); 392 } 393 394 /* With a separate thread, return the last status value. Otherwise, invoke the 395 main loop. */ 396 397 if (config->config_thread) 398 return L4_EOK; 399 else 400 return (long) ipc_server_start_mainloop(config); 401 } 402 403 /* vim: tabstop=2 expandtab shiftwidth=2 404 */