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