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