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 msg->import_item = 0; 217 218 /* Message label overriding. */ 219 220 msg->new_label = 0; 221 222 /* Server control. */ 223 224 msg->terminating = 0; 225 } 226 227 /* Wait for an incoming message. */ 228 229 void ipc_message_wait(ipc_message_t *msg, l4_umword_t *label) 230 { 231 /* Since no receive flexpages are expected when waiting for incoming messages, 232 the item count is reset to zero. */ 233 234 msg->receive_items = 0; 235 ipc_message_restore_buffer_registers(msg); 236 msg->tag = l4_ipc_wait(l4_utcb(), label, L4_IPC_NEVER); 237 } 238 239 240 241 /* Add a capability to the message. */ 242 243 void ipc_message_add_capability(ipc_message_t *msg, l4_cap_idx_t cap) 244 { 245 ipc_message_export_capability(msg, msg->items++, cap); 246 247 /* NOTE: Might use the "grant" operation instead of explicitly discarding. */ 248 249 if (cap & IPC_DISCARD_CAP_FLAG) 250 ipc_message_discard_capability(msg, cap & ~IPC_DISCARD_CAP_FLAG); 251 } 252 253 /* Add an item to the message. */ 254 255 void ipc_message_add_item(ipc_message_t *msg, l4_cap_idx_t cap) 256 { 257 ipc_message_export_capability(msg, msg->items++, cap); 258 259 /* NOTE: Might use the "grant" operation instead of explicitly discarding. */ 260 261 if (cap & IPC_DISCARD_CAP_FLAG) 262 ipc_message_discard_capability(msg, cap & ~IPC_DISCARD_CAP_FLAG); 263 } 264 265 /* Add a flexpage to the message using a type to combine the "hot spot" and 266 flexpage. */ 267 268 void ipc_message_add_fpage(ipc_message_t *msg, l4_snd_fpage_t fpage) 269 { 270 /* Test for {0, l4_fpage_invalid()} as the flexpage to avoid setting an 271 optional flexpage result, useful for the page fault handler. */ 272 273 if (fpage.snd_base || fpage.fpage.raw) 274 ipc_message_export_fpage(msg, msg->items++, fpage); 275 } 276 277 /* Add the expectation of a capability. Note that a capability is accepted as a 278 parameter but ignored, this being for the convenience of code generation. */ 279 280 void ipc_message_add_receive_capability(ipc_message_t *msg, l4_cap_idx_t cap) 281 { 282 (void) cap; 283 _expect_capability(&msg->bregs, msg->receive_items++); 284 } 285 286 /* Add a receive window flexpage item to the message. */ 287 288 void ipc_message_add_receive_fpage(ipc_message_t *msg, l4_snd_fpage_t fpage) 289 { 290 ipc_message_receive_fpage(msg, msg->receive_items++, fpage); 291 } 292 293 /* Add a flexpage to the message. */ 294 295 void ipc_message_add_page(ipc_message_t *msg, l4_umword_t hot_spot, 296 l4_fpage_t fpage) 297 { 298 ipc_message_export_page(msg, msg->items++, hot_spot, fpage); 299 } 300 301 /* Add a word value to the message. */ 302 303 void ipc_message_add_word(ipc_message_t *msg, l4_umword_t value) 304 { 305 /* NOTE: Should raise an exception if there are items. */ 306 307 if (!msg->items) 308 msg->mregs.mr[msg->words++] = value; 309 } 310 311 /* Add the given dimensioned data to the message. */ 312 313 void ipc_message_add_data(ipc_message_t *msg, const char *value, size_t length) 314 { 315 void *target = ipc_message_reserve_data(msg, length); 316 317 memcpy(target, value, length); 318 } 319 320 /* Add the given null-terminated character string to the message. */ 321 322 void ipc_message_add_string(ipc_message_t *msg, const char *value) 323 { 324 ipc_message_add_data(msg, value, strlen(value) + 1); 325 } 326 327 /* Propagate the given item to the message. */ 328 329 void ipc_message_propagate_item(ipc_message_t *msg, l4_cap_idx_t cap) 330 { 331 ipc_message_propagate_capability(msg, msg->items++, cap); 332 } 333 334 /* Indicate an error condition in the message. */ 335 336 void ipc_message_send_error(ipc_message_t *msg, long error) 337 { 338 msg->new_label = error; 339 } 340 341 /* Return a word value from the message. */ 342 343 l4_umword_t ipc_message_get_word(ipc_message_t *msg, unsigned int word) 344 { 345 return msg->mregs.mr[word]; 346 } 347 348 /* Return a pointer to a word in the message. */ 349 350 l4_umword_t *ipc_message_get_word_address(ipc_message_t *msg, unsigned int word) 351 { 352 return &msg->mregs.mr[word]; 353 } 354 355 /* Return the number of received items. */ 356 357 unsigned int ipc_message_number_of_items(ipc_message_t *msg) 358 { 359 return l4_msgtag_items(msg->tag); 360 } 361 362 /* Return the number of received words. */ 363 364 unsigned int ipc_message_number_of_words(ipc_message_t *msg) 365 { 366 return l4_msgtag_words(msg->tag); 367 } 368 369 /* Message tag generation. */ 370 371 l4_msgtag_t ipc_message_reply_tag(ipc_message_t *msg) 372 { 373 return l4_msgtag(msg->new_label, msg->words, msg->items, 0); 374 } 375 376 l4_msgtag_t ipc_message_request_tag(ipc_message_t *msg, int op) 377 { 378 return l4_msgtag(op, msg->words, msg->items, 0); 379 } 380 381 /* Reserve space for dimensioned data, returning a pointer to use to add the 382 data. The length of the data is incorporated into the message. */ 383 384 void *ipc_message_reserve_data(ipc_message_t *msg, size_t length) 385 { 386 /* Indicate the length of the data. */ 387 388 ipc_message_add_word(msg, length); 389 390 /* Reserve the space. */ 391 392 return ipc_message_reserve_words(msg, length); 393 } 394 395 /* Reserve space for dimensioned data, returning a pointer to use to add the 396 data. The length is not incorporated into the message. */ 397 398 void *ipc_message_reserve_words(ipc_message_t *msg, size_t length) 399 { 400 /* Calculate the rounded-up size of the data and the necessary padding. */ 401 402 size_t size = get_size_in_words(length); 403 size_t padding = size - length; 404 void *target; 405 406 /* NOTE: Should test the length against the capacity of the message. */ 407 408 /* Obtain the location of the next word, where the data will be copied. */ 409 410 target = &msg->mregs.mr[msg->words]; 411 412 /* Pad the data and update the number of words. */ 413 414 memset(target + length, 0, padding); 415 msg->words += size / sizeof(l4_umword_t); 416 417 return target; 418 } 419 420 421 422 /* Discard a capability after replying. */ 423 424 void ipc_message_discard_capability(ipc_message_t *msg, l4_cap_idx_t cap) 425 { 426 msg->to_discard[msg->discarded_items++] = cap; 427 } 428 429 /* Discard a dataspace. */ 430 431 void ipc_message_discard_dataspace(ipc_message_t *msg, l4re_ds_t mem, l4_addr_t addr) 432 { 433 ipc_message_discard_capability(msg, mem); 434 ipc_detach_dataspace((void *) addr); 435 } 436 437 /* Reserve the given number slots from zero for incoming capabilities. */ 438 439 long ipc_message_expect_capabilities(ipc_message_t *msg, int number) 440 { 441 msg->expected_items = number; 442 return _expect_capabilities(&msg->bregs, number); 443 } 444 445 /* Reserve a slot for an incoming capability. */ 446 447 long ipc_message_expect_capability(ipc_message_t *msg, int item) 448 { 449 if (item >= (int) msg->expected_items) 450 msg->expected_items = item + 1; 451 452 return _expect_capability(&msg->bregs, item); 453 } 454 455 /* Export a capability at the given position in the message. */ 456 457 void ipc_message_export_capability(ipc_message_t *msg, int item, l4_cap_idx_t ref) 458 { 459 if (l4_is_valid_cap(ref)) 460 { 461 msg->mregs.mr[msg->words + item * 2] = 0 | L4_ITEM_MAP; 462 msg->mregs.mr[msg->words + item * 2 + 1] = l4_obj_fpage(ref, 0, L4_FPAGE_RWX).raw; 463 } 464 else 465 { 466 msg->mregs.mr[msg->words + item * 2] = 0; 467 msg->mregs.mr[msg->words + item * 2 + 1] = 0; 468 } 469 } 470 471 /* Export a flexpage at the given position in the message. Here, the snd_base 472 member of the flexpage structure is used to hold the "hot spot" value. */ 473 474 void ipc_message_export_fpage(ipc_message_t *msg, int item, l4_snd_fpage_t fpage) 475 { 476 msg->mregs.mr[msg->words + item * 2] = l4_map_control(fpage.snd_base, L4_FPAGE_CACHEABLE, 0); 477 msg->mregs.mr[msg->words + item * 2 + 1] = fpage.fpage.raw; 478 } 479 480 /* Export a flexpage at the given position in the message. */ 481 482 void ipc_message_export_page(ipc_message_t *msg, int item, l4_umword_t hot_spot, l4_fpage_t fpage) 483 { 484 msg->mregs.mr[msg->words + item * 2] = l4_map_control(hot_spot, L4_FPAGE_CACHEABLE, 0); 485 msg->mregs.mr[msg->words + item * 2 + 1] = fpage.raw; 486 } 487 488 /* Specify a receive window flexpage item in the message. */ 489 490 void ipc_message_receive_fpage(ipc_message_t *msg, int item, l4_snd_fpage_t fpage) 491 { 492 msg->bregs.br[item * 2] = l4_map_control(fpage.snd_base, 0, 0); 493 msg->bregs.br[item * 2 + 1] = fpage.fpage.raw; 494 } 495 496 497 498 /* Import from the message the capability at the given item position, updating 499 the buffer registers for future capabilities. */ 500 501 long ipc_message_import_and_expect_capability(ipc_message_t *msg, int item, l4_cap_idx_t *ref) 502 { 503 long err; 504 int local; 505 506 err = _import_capability(msg->tag, &msg->bregs, &msg->mregs, item, msg->import_item, ref, &local); 507 if (err) 508 return err; 509 510 if (local) 511 return L4_EOK; 512 513 err = ipc_message_expect_capability(msg, msg->import_item); 514 msg->import_item += 1; 515 return err; 516 } 517 518 /* Import from the message a dataspace, mapping it to an address, updating the 519 buffer registers for future capabilities. */ 520 521 long ipc_message_import_and_expect_dataspace(ipc_message_t *msg, int item, l4re_ds_t *mem, l4_addr_t *addr) 522 { 523 long err; 524 int local; 525 526 err = _import_dataspace(msg->tag, &msg->bregs, &msg->mregs, item, msg->import_item, mem, addr, &local); 527 if (err) 528 return err; 529 530 if (local) 531 return L4_EOK; 532 533 err = ipc_message_expect_capability(msg, msg->import_item); 534 msg->import_item += 1; 535 return err; 536 } 537 538 /* Import from the message the capability at the given item position. */ 539 540 long ipc_message_import_capability(ipc_message_t *msg, int item, l4_cap_idx_t *ref) 541 { 542 int local; 543 long err; 544 545 err = _import_capability(msg->tag, &msg->bregs, &msg->mregs, item, msg->import_item, ref, &local); 546 if (err) 547 return err; 548 549 msg->import_item += local ? 0 : 1; 550 return L4_EOK; 551 } 552 553 /* Import from the message a dataspace, mapping it to an address. */ 554 555 long ipc_message_import_dataspace(ipc_message_t *msg, int item, l4re_ds_t *mem, l4_addr_t *addr) 556 { 557 int local; 558 long err; 559 560 err = _import_dataspace(msg->tag, &msg->bregs, &msg->mregs, item, msg->import_item, mem, addr, &local); 561 if (err) 562 return err; 563 564 msg->import_item += local ? 0 : 1; 565 return L4_EOK; 566 } 567 568 /* Import from the message the flexpage at the given item position. */ 569 570 long ipc_message_import_fpage(ipc_message_t *msg, int item, l4_snd_fpage_t *fpage) 571 { 572 return _import_fpage(msg->tag, &msg->bregs, &msg->mregs, item, fpage); 573 } 574 575 /* Export a capability, discarding it by propagating it to the recipient. */ 576 577 void ipc_message_propagate_capability(ipc_message_t *msg, int item, l4_cap_idx_t ref) 578 { 579 ipc_message_export_capability(msg, item, ref); 580 ipc_message_discard_capability(msg, ref); 581 } 582 583 /* vim: tabstop=2 expandtab shiftwidth=2 584 */