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