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 if (cap & IPC_DISCARD_CAP_FLAG) 240 ipc_message_discard_capability(msg, cap & ~IPC_DISCARD_CAP_FLAG); 241 } 242 243 /* Add an item to the message. */ 244 245 void ipc_message_add_item(ipc_message_t *msg, l4_cap_idx_t cap) 246 { 247 ipc_message_export_capability(msg, msg->items++, cap); 248 if (cap & IPC_DISCARD_CAP_FLAG) 249 ipc_message_discard_capability(msg, cap & ~IPC_DISCARD_CAP_FLAG); 250 } 251 252 /* Add a flexpage to the message using a type to combine the "hot spot" and 253 flexpage. */ 254 255 void ipc_message_add_fpage(ipc_message_t *msg, l4_snd_fpage_t fpage) 256 { 257 /* Test for {0, l4_fpage_invalid()} as the flexpage to avoid setting an 258 optional flexpage result, useful for the page fault handler. */ 259 260 if (fpage.snd_base || fpage.fpage.raw) 261 ipc_message_export_fpage(msg, msg->items++, fpage); 262 } 263 264 /* Add a receive window flexpage item to the message. */ 265 266 void ipc_message_add_receive_fpage(ipc_message_t *msg, l4_snd_fpage_t fpage) 267 { 268 ipc_message_receive_fpage(msg, msg->receive_items++, fpage); 269 } 270 271 /* Add a flexpage to the message. */ 272 273 void ipc_message_add_page(ipc_message_t *msg, l4_umword_t hot_spot, 274 l4_fpage_t fpage) 275 { 276 ipc_message_export_page(msg, msg->items++, hot_spot, fpage); 277 } 278 279 /* Add a word value to the message. */ 280 281 void ipc_message_add_word(ipc_message_t *msg, l4_umword_t value) 282 { 283 /* NOTE: Should raise an exception if there are items. */ 284 285 if (!msg->items) 286 msg->mregs.mr[msg->words++] = value; 287 } 288 289 /* Add the given dimensioned data to the message. */ 290 291 void ipc_message_add_data(ipc_message_t *msg, const char *value, size_t length) 292 { 293 void *target = ipc_message_reserve_data(msg, length); 294 295 memcpy(target, value, length); 296 } 297 298 /* Add the given null-terminated character string to the message. */ 299 300 void ipc_message_add_string(ipc_message_t *msg, const char *value) 301 { 302 ipc_message_add_data(msg, value, strlen(value) + 1); 303 } 304 305 /* Propagate the given item to the message. */ 306 307 void ipc_message_propagate_item(ipc_message_t *msg, l4_cap_idx_t cap) 308 { 309 ipc_message_propagate_capability(msg, msg->items++, cap); 310 } 311 312 /* Indicate an error condition in the message. */ 313 314 void ipc_message_send_error(ipc_message_t *msg, long error) 315 { 316 msg->new_label = error; 317 } 318 319 /* Return a word value from the message. */ 320 321 l4_umword_t ipc_message_get_word(ipc_message_t *msg, unsigned int word) 322 { 323 return msg->mregs.mr[word]; 324 } 325 326 /* Return a pointer to a word in the message. */ 327 328 l4_umword_t *ipc_message_get_word_address(ipc_message_t *msg, unsigned int word) 329 { 330 return &msg->mregs.mr[word]; 331 } 332 333 /* Return the number of received items. */ 334 335 unsigned int ipc_message_number_of_items(ipc_message_t *msg) 336 { 337 return l4_msgtag_items(msg->tag); 338 } 339 340 /* Return the number of received words. */ 341 342 unsigned int ipc_message_number_of_words(ipc_message_t *msg) 343 { 344 return l4_msgtag_words(msg->tag); 345 } 346 347 /* Message tag generation. */ 348 349 l4_msgtag_t ipc_message_reply_tag(ipc_message_t *msg) 350 { 351 return l4_msgtag(msg->new_label, msg->words, msg->items, 0); 352 } 353 354 l4_msgtag_t ipc_message_request_tag(ipc_message_t *msg, int op) 355 { 356 return l4_msgtag(op, msg->words, msg->items, 0); 357 } 358 359 /* Reserve space for dimensioned data, returning a pointer to use to add the 360 data. The length of the data is incorporated into the message. */ 361 362 void *ipc_message_reserve_data(ipc_message_t *msg, size_t length) 363 { 364 /* Indicate the length of the data. */ 365 366 ipc_message_add_word(msg, length); 367 368 /* Reserve the space. */ 369 370 return ipc_message_reserve_words(msg, length); 371 } 372 373 /* Reserve space for dimensioned data, returning a pointer to use to add the 374 data. The length is not incorporated into the message. */ 375 376 void *ipc_message_reserve_words(ipc_message_t *msg, size_t length) 377 { 378 /* Calculate the rounded-up size of the data and the necessary padding. */ 379 380 size_t size = get_size_in_words(length); 381 size_t padding = size - length; 382 void *target; 383 384 /* NOTE: Should test the length against the capacity of the message. */ 385 386 /* Obtain the location of the next word, where the data will be copied. */ 387 388 target = &msg->mregs.mr[msg->words]; 389 390 /* Pad the data and update the number of words. */ 391 392 memset(target + length, 0, padding); 393 msg->words += size / sizeof(l4_umword_t); 394 395 return target; 396 } 397 398 399 400 /* Discard a capability after replying. */ 401 402 void ipc_message_discard_capability(ipc_message_t *msg, l4_cap_idx_t cap) 403 { 404 msg->to_discard[msg->discarded_items++] = cap; 405 } 406 407 /* Discard a dataspace. */ 408 409 void ipc_message_discard_dataspace(ipc_message_t *msg, l4re_ds_t mem, l4_addr_t addr) 410 { 411 ipc_message_discard_capability(msg, mem); 412 ipc_detach_dataspace((void *) addr); 413 } 414 415 /* Reserve the given number slots from zero for incoming capabilities. */ 416 417 long ipc_message_expect_capabilities(ipc_message_t *msg, int number) 418 { 419 msg->expected_items = number; 420 return _expect_capabilities(&msg->bregs, number); 421 } 422 423 /* Reserve a slot for an incoming capability. */ 424 425 long ipc_message_expect_capability(ipc_message_t *msg, int item) 426 { 427 if (item >= (int) msg->expected_items) 428 msg->expected_items = item + 1; 429 430 return _expect_capability(&msg->bregs, item); 431 } 432 433 /* Export a capability at the given position in the message. */ 434 435 void ipc_message_export_capability(ipc_message_t *msg, int item, l4_cap_idx_t ref) 436 { 437 msg->mregs.mr[msg->words + item * 2] = 0 | L4_ITEM_MAP; 438 msg->mregs.mr[msg->words + item * 2 + 1] = l4_obj_fpage(ref, 0, L4_FPAGE_RWX).raw; 439 } 440 441 /* Export a flexpage at the given position in the message. Here, the snd_base 442 member of the flexpage structure is used to hold the "hot spot" value. */ 443 444 void ipc_message_export_fpage(ipc_message_t *msg, int item, l4_snd_fpage_t fpage) 445 { 446 msg->mregs.mr[msg->words + item * 2] = l4_map_control(fpage.snd_base, L4_FPAGE_CACHEABLE, 0); 447 msg->mregs.mr[msg->words + item * 2 + 1] = fpage.fpage.raw; 448 } 449 450 /* Export a flexpage at the given position in the message. */ 451 452 void ipc_message_export_page(ipc_message_t *msg, int item, l4_umword_t hot_spot, l4_fpage_t fpage) 453 { 454 msg->mregs.mr[msg->words + item * 2] = l4_map_control(hot_spot, L4_FPAGE_CACHEABLE, 0); 455 msg->mregs.mr[msg->words + item * 2 + 1] = fpage.raw; 456 } 457 458 /* Specify a receive window flexpage item in the message. */ 459 460 void ipc_message_receive_fpage(ipc_message_t *msg, int item, l4_snd_fpage_t fpage) 461 { 462 msg->bregs.br[item * 2] = l4_map_control(fpage.snd_base, 0, 0); 463 msg->bregs.br[item * 2 + 1] = fpage.fpage.raw; 464 } 465 466 467 468 /* Import from the message the capability at the given item position, updating 469 the buffer registers for future capabilities. */ 470 471 long ipc_message_import_capability(ipc_message_t *msg, int item, l4_cap_idx_t *ref) 472 { 473 long err; 474 475 err = _import_capability(msg->tag, &msg->bregs, &msg->mregs, item, ref); 476 if (err) 477 return err; 478 479 return ipc_message_expect_capability(msg, item); 480 } 481 482 /* Import from the message a dataspace, mapping it to an address, updating the 483 buffer registers for future capabilities. */ 484 485 long ipc_message_import_dataspace(ipc_message_t *msg, int item, l4re_ds_t *mem, l4_addr_t *addr) 486 { 487 long err; 488 489 err = _import_dataspace(msg->tag, &msg->bregs, &msg->mregs, item, mem, addr); 490 if (err) 491 return err; 492 493 return ipc_message_expect_capability(msg, item); 494 } 495 496 /* Import from the message the flexpage at the given item position. */ 497 498 long ipc_message_import_fpage(ipc_message_t *msg, int item, l4_snd_fpage_t *fpage) 499 { 500 return _import_fpage(msg->tag, &msg->bregs, &msg->mregs, item, fpage); 501 } 502 503 /* Export a capability, discarding it by propagating it to the recipient. */ 504 505 void ipc_message_propagate_capability(ipc_message_t *msg, int item, l4_cap_idx_t ref) 506 { 507 ipc_message_export_capability(msg, item, ref); 508 ipc_message_discard_capability(msg, ref); 509 } 510 511 /* vim: tabstop=2 expandtab shiftwidth=2 512 */