1 /* 2 * Interprocess communication message abstraction. 3 * 4 * Copyright (C) 2018, 2019, 2021-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 <string.h> 23 24 #include "cap_alloc.h" 25 #include "message.h" 26 #include "mem_ipc.h" 27 #include "util_ipc.h" 28 29 30 31 /* Calculate the given length rounded up to whole word increments. */ 32 33 static size_t get_size_in_words(size_t length) 34 { 35 size_t mask = sizeof(l4_umword_t) - 1; 36 return (length + mask) & ~mask; 37 } 38 39 40 41 /* Message operations. */ 42 43 /* Discard capabilities for transfer to other tasks. */ 44 45 void ipc_message_discard(ipc_message_t *msg) 46 { 47 unsigned int i; 48 49 for (i = 0; i < msg->discarded_items; i++) 50 ipc_cap_free_um(msg->to_discard[i]); 51 } 52 53 /* Initialise a message structure with the given number of expected items. This 54 function is primarily for the use of servers needing to reserve items that 55 might be sent in messages from clients. */ 56 57 long ipc_message_expect(ipc_message_t *msg, unsigned int expected_items) 58 { 59 return ipc_message_expect_capabilities(msg, expected_items); 60 } 61 62 /* Free capabilities expected in messages. */ 63 64 void ipc_message_free(ipc_message_t *msg) 65 { 66 _free_expected_capabilities(&msg->bregs, msg->import_item, msg->expected_items); 67 } 68 69 /* Clear message attributes for sending and response handling. */ 70 71 void ipc_message_new(ipc_message_t *msg) 72 { 73 /* Set a default for expected items. */ 74 75 msg->expected_items = 0; 76 msg->receive_items = 0; 77 ipc_message_reset(msg); 78 } 79 80 /* Open an incoming message by resetting sending state and preserving the 81 message registers plus the buffer registers for the expected number of 82 items. */ 83 84 void ipc_message_open(ipc_message_t *msg) 85 { 86 ipc_message_reset(msg); 87 ipc_message_preserve_buffer_registers(msg); 88 ipc_message_preserve_message_registers(msg); 89 } 90 91 /* Preserve the buffer registers. */ 92 93 void ipc_message_preserve_buffer_registers(ipc_message_t *msg) 94 { 95 unsigned int i; 96 97 /* Reference the UTCB virtual registers. */ 98 99 l4_buf_regs_t *bregs = l4_utcb_br(); 100 101 msg->bregs.bdr = bregs->bdr; 102 103 /* NOTE: Support a mixture of expected items and received flexpages. */ 104 105 if ((msg->expected_items) || (msg->receive_items)) 106 { 107 for (i = 0; (i < msg->expected_items) || (i < msg->receive_items * 2); i++) 108 msg->bregs.br[i] = bregs->br[i]; 109 } 110 } 111 112 /* Preserve the message registers. */ 113 114 void ipc_message_preserve_message_registers(ipc_message_t *msg) 115 { 116 /* Compute the number of message register words affected. */ 117 118 unsigned int i, affected_words = l4_msgtag_words(msg->tag) + 119 2 * l4_msgtag_items(msg->tag); 120 121 /* Reference the UTCB virtual registers. */ 122 123 l4_msg_regs_t *mregs = l4_utcb_mr(); 124 125 /* Copy message register information. */ 126 127 for (i = 0; i < affected_words; i++) 128 msg->mregs.mr[i] = mregs->mr[i]; 129 } 130 131 /* Restore the message and buffer registers. */ 132 133 void ipc_message_prepare(ipc_message_t *msg) 134 { 135 ipc_message_restore_buffer_registers(msg); 136 ipc_message_restore_message_registers(msg); 137 } 138 139 /* Restore buffer registers so that item expectations can be set. */ 140 141 void ipc_message_restore_buffer_registers(ipc_message_t *msg) 142 { 143 unsigned int i; 144 145 /* Reference the UTCB virtual registers. */ 146 147 l4_buf_regs_t *bregs = l4_utcb_br(); 148 149 /* Restore buffer register information. */ 150 151 bregs->bdr = msg->bregs.bdr; 152 153 /* NOTE: Support a mixture of expected items and received flexpages. */ 154 155 if ((msg->expected_items) || (msg->receive_items)) 156 { 157 for (i = 0; (i < msg->expected_items) || (i < msg->receive_items * 2); i++) 158 bregs->br[i] = msg->bregs.br[i]; 159 } 160 } 161 162 /* Restore message registers to communicate recorded data and items. */ 163 164 void ipc_message_restore_message_registers(ipc_message_t *msg) 165 { 166 /* Compute the number of message register words affected. */ 167 168 unsigned int i, affected_words = msg->words + 2 * msg->items; 169 170 /* Reference the UTCB virtual registers. */ 171 172 l4_msg_regs_t *mregs = l4_utcb_mr(); 173 174 /* Restore message register information. */ 175 176 for (i = 0; i < affected_words; i++) 177 mregs->mr[i] = msg->mregs.mr[i]; 178 } 179 180 /* Prepare and send a reply using the message. */ 181 182 void ipc_message_reply(ipc_message_t *msg) 183 { 184 ipc_message_prepare(msg); 185 msg->tag = util_ipc_reply(ipc_message_reply_tag(msg)); 186 } 187 188 /* Prepare and send a request using the message, involving the given operation, 189 directed at the indicated endpoint. Open a message for the reply. */ 190 191 void ipc_message_request(ipc_message_t *msg, int op, l4_cap_idx_t endpoint) 192 { 193 ipc_message_prepare(msg); 194 msg->tag = util_ipc_request(ipc_message_request_tag(msg, op), endpoint); 195 ipc_message_open(msg); 196 } 197 198 /* Prepare and send a message, involving the given operation, directed at the 199 indicated endpoint. No reply is expected. */ 200 201 void ipc_message_send(ipc_message_t *msg, int op, l4_cap_idx_t endpoint) 202 { 203 ipc_message_prepare(msg); 204 msg->tag = util_ipc_send(ipc_message_request_tag(msg, op), endpoint); 205 } 206 207 /* Reset the state of the message for sending purposes. */ 208 209 void ipc_message_reset(ipc_message_t *msg) 210 { 211 /* Initialise words and items for sending. */ 212 213 msg->discarded_items = 0; 214 msg->words = 0; 215 msg->items = 0; 216 217 /* Initialise items to be received. 218 NOTE: This does not support item type mixing. */ 219 220 msg->import_item = 0; 221 msg->bregs.bdr = 0; 222 223 /* Message label overriding. */ 224 225 msg->new_label = 0; 226 227 /* Server control. */ 228 229 msg->terminating = 0; 230 } 231 232 /* Wait for an incoming message. */ 233 234 void ipc_message_wait(ipc_message_t *msg, l4_umword_t *label) 235 { 236 /* Since no receive flexpages are expected when waiting for incoming messages, 237 the item count is reset to zero. */ 238 239 msg->receive_items = 0; 240 ipc_message_restore_buffer_registers(msg); 241 msg->tag = l4_ipc_wait(l4_utcb(), label, L4_IPC_NEVER); 242 } 243 244 245 246 /* Add a capability to the message. */ 247 248 void ipc_message_add_capability(ipc_message_t *msg, l4_cap_idx_t cap) 249 { 250 ipc_message_export_capability(msg, msg->items++, cap); 251 252 /* NOTE: Might use the "grant" operation instead of explicitly discarding. */ 253 254 if (cap & IPC_DISCARD_CAP_FLAG) 255 ipc_message_discard_capability(msg, cap & ~IPC_DISCARD_CAP_FLAG); 256 } 257 258 /* Add an item to the message. */ 259 260 void ipc_message_add_item(ipc_message_t *msg, l4_cap_idx_t cap) 261 { 262 ipc_message_export_capability(msg, msg->items++, cap); 263 264 /* NOTE: Might use the "grant" operation instead of explicitly discarding. */ 265 266 if (cap & IPC_DISCARD_CAP_FLAG) 267 ipc_message_discard_capability(msg, cap & ~IPC_DISCARD_CAP_FLAG); 268 } 269 270 /* Add a flexpage to the message using a type to combine the "hot spot" and 271 flexpage. */ 272 273 void ipc_message_add_fpage(ipc_message_t *msg, l4_snd_fpage_t fpage) 274 { 275 /* Test for {0, l4_fpage_invalid()} as the flexpage to avoid setting an 276 optional flexpage result, useful for the page fault handler. */ 277 278 if (fpage.snd_base || fpage.fpage.raw) 279 ipc_message_export_fpage(msg, msg->items++, fpage); 280 } 281 282 /* Add the expectation of a capability. Note that a capability is accepted as a 283 parameter but ignored, this being for the convenience of code generation. */ 284 285 void ipc_message_add_receive_capability(ipc_message_t *msg, l4_cap_idx_t cap) 286 { 287 (void) cap; 288 _expect_capability(&msg->bregs, msg->receive_items++); 289 } 290 291 /* Add a receive window flexpage item to the message. */ 292 293 void ipc_message_add_receive_fpage(ipc_message_t *msg, l4_snd_fpage_t fpage) 294 { 295 ipc_message_receive_fpage(msg, msg->receive_items++, fpage); 296 } 297 298 /* Add a flexpage to the message. */ 299 300 void ipc_message_add_page(ipc_message_t *msg, l4_umword_t hot_spot, 301 l4_fpage_t fpage) 302 { 303 ipc_message_export_page(msg, msg->items++, hot_spot, fpage); 304 } 305 306 /* Add a word value to the message. */ 307 308 void ipc_message_add_word(ipc_message_t *msg, l4_umword_t value) 309 { 310 /* NOTE: Should raise an exception if there are items. */ 311 312 if (!msg->items) 313 msg->mregs.mr[msg->words++] = value; 314 } 315 316 /* Add the given dimensioned data to the message. */ 317 318 void ipc_message_add_data(ipc_message_t *msg, const char *value, size_t length) 319 { 320 void *target = ipc_message_reserve_data(msg, length); 321 322 memcpy(target, value, length); 323 } 324 325 /* Add the given null-terminated character string to the message. */ 326 327 void ipc_message_add_string(ipc_message_t *msg, const char *value) 328 { 329 ipc_message_add_data(msg, value, strlen(value) + 1); 330 } 331 332 /* Propagate the given item to the message. */ 333 334 void ipc_message_propagate_item(ipc_message_t *msg, l4_cap_idx_t cap) 335 { 336 ipc_message_propagate_capability(msg, msg->items++, cap); 337 } 338 339 /* Indicate an error condition in the message. */ 340 341 void ipc_message_send_error(ipc_message_t *msg, long error) 342 { 343 msg->new_label = error; 344 } 345 346 /* Return a word value from the message. */ 347 348 l4_umword_t ipc_message_get_word(ipc_message_t *msg, unsigned int word) 349 { 350 return msg->mregs.mr[word]; 351 } 352 353 /* Return a pointer to a word in the message. */ 354 355 l4_umword_t *ipc_message_get_word_address(ipc_message_t *msg, unsigned int word) 356 { 357 return &msg->mregs.mr[word]; 358 } 359 360 /* Return the number of received items. */ 361 362 unsigned int ipc_message_number_of_items(ipc_message_t *msg) 363 { 364 return l4_msgtag_items(msg->tag); 365 } 366 367 /* Return the number of received words. */ 368 369 unsigned int ipc_message_number_of_words(ipc_message_t *msg) 370 { 371 return l4_msgtag_words(msg->tag); 372 } 373 374 /* Message tag generation. */ 375 376 l4_msgtag_t ipc_message_reply_tag(ipc_message_t *msg) 377 { 378 return l4_msgtag(msg->new_label, msg->words, msg->items, 0); 379 } 380 381 l4_msgtag_t ipc_message_request_tag(ipc_message_t *msg, int op) 382 { 383 return l4_msgtag(op, msg->words, msg->items, 0); 384 } 385 386 /* Reserve space for dimensioned data, returning a pointer to use to add the 387 data. The length of the data is incorporated into the message. */ 388 389 void *ipc_message_reserve_data(ipc_message_t *msg, size_t length) 390 { 391 /* Indicate the length of the data. */ 392 393 ipc_message_add_word(msg, length); 394 395 /* Reserve the space. */ 396 397 return ipc_message_reserve_words(msg, length); 398 } 399 400 /* Reserve space for dimensioned data, returning a pointer to use to add the 401 data. The length is not incorporated into the message. */ 402 403 void *ipc_message_reserve_words(ipc_message_t *msg, size_t length) 404 { 405 /* Calculate the rounded-up size of the data and the necessary padding. */ 406 407 size_t size = get_size_in_words(length); 408 size_t padding = size - length; 409 void *target; 410 411 /* NOTE: Should test the length against the capacity of the message. */ 412 413 /* Obtain the location of the next word, where the data will be copied. */ 414 415 target = &msg->mregs.mr[msg->words]; 416 417 /* Pad the data and update the number of words. */ 418 419 memset(target + length, 0, padding); 420 msg->words += size / sizeof(l4_umword_t); 421 422 return target; 423 } 424 425 426 427 /* Discard a capability after replying. */ 428 429 void ipc_message_discard_capability(ipc_message_t *msg, l4_cap_idx_t cap) 430 { 431 msg->to_discard[msg->discarded_items++] = cap; 432 } 433 434 /* Discard a dataspace. */ 435 436 void ipc_message_discard_dataspace(ipc_message_t *msg, l4_cap_idx_t mem, l4_addr_t addr) 437 { 438 ipc_message_discard_capability(msg, mem); 439 ipc_detach_dataspace((void *) addr); 440 } 441 442 /* Reserve the given number slots from zero for incoming capabilities. */ 443 444 long ipc_message_expect_capabilities(ipc_message_t *msg, int number) 445 { 446 msg->expected_items = number; 447 return _expect_capabilities(&msg->bregs, number); 448 } 449 450 /* Reserve a slot for an incoming capability. */ 451 452 long ipc_message_expect_capability(ipc_message_t *msg, int item) 453 { 454 if (item >= (int) msg->expected_items) 455 msg->expected_items = item + 1; 456 457 return _expect_capability(&msg->bregs, item); 458 } 459 460 /* Export a capability at the given position in the message. */ 461 462 void ipc_message_export_capability(ipc_message_t *msg, int item, l4_cap_idx_t ref) 463 { 464 if (l4_is_valid_cap(ref)) 465 { 466 msg->mregs.mr[msg->words + item * 2] = 0 | L4_ITEM_MAP; 467 msg->mregs.mr[msg->words + item * 2 + 1] = l4_obj_fpage(ref, 0, L4_FPAGE_RWX).raw; 468 } 469 else 470 { 471 msg->mregs.mr[msg->words + item * 2] = 0; 472 msg->mregs.mr[msg->words + item * 2 + 1] = 0; 473 } 474 } 475 476 /* Export a flexpage at the given position in the message. Here, the snd_base 477 member of the flexpage structure is used to hold the "hot spot" value. */ 478 479 void ipc_message_export_fpage(ipc_message_t *msg, int item, l4_snd_fpage_t fpage) 480 { 481 msg->mregs.mr[msg->words + item * 2] = l4_map_control(fpage.snd_base, L4_FPAGE_CACHEABLE, 0); 482 msg->mregs.mr[msg->words + item * 2 + 1] = fpage.fpage.raw; 483 } 484 485 /* Export a flexpage at the given position in the message. */ 486 487 void ipc_message_export_page(ipc_message_t *msg, int item, l4_umword_t hot_spot, l4_fpage_t fpage) 488 { 489 msg->mregs.mr[msg->words + item * 2] = l4_map_control(hot_spot, L4_FPAGE_CACHEABLE, 0); 490 msg->mregs.mr[msg->words + item * 2 + 1] = fpage.raw; 491 } 492 493 /* Specify a receive window flexpage item in the message. */ 494 495 void ipc_message_receive_fpage(ipc_message_t *msg, int item, l4_snd_fpage_t fpage) 496 { 497 msg->bregs.br[item * 2] = l4_map_control(fpage.snd_base, 0, 0); 498 msg->bregs.br[item * 2 + 1] = fpage.fpage.raw; 499 } 500 501 502 503 /* Import from the message the capability at the given item position, updating 504 the buffer registers for future capabilities. */ 505 506 long ipc_message_import_and_expect_capability(ipc_message_t *msg, int item, l4_cap_idx_t *ref) 507 { 508 long err; 509 int local; 510 511 err = _import_capability(msg->tag, &msg->bregs, &msg->mregs, item, msg->import_item, ref, &local); 512 if (err) 513 return err; 514 515 if (local) 516 return L4_EOK; 517 518 /* Only if the specified item yielded a valid capability, will an allocated 519 have been consumed. Otherwise, the next allocated capability will still be 520 available. */ 521 522 if (l4_is_valid_cap(*ref)) 523 { 524 err = ipc_message_expect_capability(msg, msg->import_item); 525 msg->import_item += 1; 526 } 527 528 return err; 529 } 530 531 /* Import from the message a dataspace, mapping it to an address, updating the 532 buffer registers for future capabilities. */ 533 534 long ipc_message_import_and_expect_dataspace(ipc_message_t *msg, int item, l4_cap_idx_t *mem, l4_addr_t *addr) 535 { 536 long err; 537 int local; 538 539 err = _import_dataspace(msg->tag, &msg->bregs, &msg->mregs, item, msg->import_item, mem, addr, &local); 540 if (err) 541 return err; 542 543 if (local) 544 return L4_EOK; 545 546 err = ipc_message_expect_capability(msg, msg->import_item); 547 msg->import_item += 1; 548 return err; 549 } 550 551 /* Import from the message the capability at the given item position. */ 552 553 long ipc_message_import_capability(ipc_message_t *msg, int item, l4_cap_idx_t *ref) 554 { 555 int local; 556 long err; 557 558 err = _import_capability(msg->tag, &msg->bregs, &msg->mregs, item, msg->import_item, ref, &local); 559 if (err) 560 return err; 561 562 msg->import_item += local ? 0 : 1; 563 return L4_EOK; 564 } 565 566 /* Import from the message a dataspace, mapping it to an address. */ 567 568 long ipc_message_import_dataspace(ipc_message_t *msg, int item, l4_cap_idx_t *mem, l4_addr_t *addr) 569 { 570 int local; 571 long err; 572 573 err = _import_dataspace(msg->tag, &msg->bregs, &msg->mregs, item, msg->import_item, mem, addr, &local); 574 if (err) 575 return err; 576 577 msg->import_item += local ? 0 : 1; 578 return L4_EOK; 579 } 580 581 /* Import from the message the flexpage at the given item position. */ 582 583 long ipc_message_import_fpage(ipc_message_t *msg, int item, l4_snd_fpage_t *fpage) 584 { 585 return _import_fpage(msg->tag, &msg->bregs, &msg->mregs, item, fpage); 586 } 587 588 /* Export a capability, discarding it by propagating it to the recipient. */ 589 590 void ipc_message_propagate_capability(ipc_message_t *msg, int item, l4_cap_idx_t ref) 591 { 592 ipc_message_export_capability(msg, item, ref); 593 ipc_message_discard_capability(msg, ref); 594 } 595 596 /* vim: tabstop=2 expandtab shiftwidth=2 597 */