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