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