1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright (C) 2018 IBM Corp. 3 4 #define _GNU_SOURCE 5 #include <assert.h> 6 #include <errno.h> 7 #include <fcntl.h> 8 #include <getopt.h> 9 #include <limits.h> 10 #include <poll.h> 11 #include <stdbool.h> 12 #include <stdint.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <syslog.h> 17 #include <signal.h> 18 #include <sys/ioctl.h> 19 #include <sys/mman.h> 20 #include <sys/stat.h> 21 #include <sys/timerfd.h> 22 #include <sys/types.h> 23 #include <time.h> 24 #include <unistd.h> 25 #include <inttypes.h> 26 27 #include "mbox.h" 28 #include "common.h" 29 #include "transport_mbox.h" 30 #include "windows.h" 31 #include "lpc.h" 32 33 struct errno_map { 34 int rc; 35 int mbox_errno; 36 }; 37 38 static const struct errno_map errno_map_v1[] = { 39 { 0, MBOX_R_SUCCESS }, 40 { EACCES, MBOX_R_PARAM_ERROR }, 41 { EBUSY, MBOX_R_SYSTEM_ERROR }, 42 { EINVAL, MBOX_R_PARAM_ERROR }, 43 { EPERM, MBOX_R_PARAM_ERROR }, 44 { ETIMEDOUT, MBOX_R_TIMEOUT }, 45 { -1, MBOX_R_SYSTEM_ERROR }, 46 }; 47 48 static const struct errno_map errno_map_v2[] = { 49 { 0, MBOX_R_SUCCESS }, 50 { EACCES, MBOX_R_PARAM_ERROR }, 51 { EBUSY, MBOX_R_BUSY }, 52 { EINVAL, MBOX_R_PARAM_ERROR }, 53 { EPERM, MBOX_R_WINDOW_ERROR }, 54 { ETIMEDOUT, MBOX_R_TIMEOUT }, 55 { -1, MBOX_R_SYSTEM_ERROR }, 56 }; 57 58 static const struct errno_map *errno_maps[] = { 59 [0] = NULL, 60 [1] = errno_map_v1, 61 [2] = errno_map_v2, 62 }; 63 64 static inline int mbox_xlate_errno(struct mbox_context *context, 65 int rc) 66 { 67 const struct errno_map *entry; 68 69 rc = -rc; 70 for(entry = errno_maps[context->version]; entry->rc != -1; entry++) { 71 if (rc == entry->rc) { 72 return -entry->mbox_errno; 73 } 74 } 75 76 return -entry->mbox_errno; 77 } 78 79 /* 80 * write_bmc_event_reg() - Write to the BMC controlled status register (reg 15) 81 * @context: The mbox context pointer 82 * 83 * Return: 0 on success otherwise negative error code 84 */ 85 static int write_bmc_event_reg(struct mbox_context *context) 86 { 87 int rc; 88 89 /* Seek mbox registers */ 90 rc = lseek(context->fds[MBOX_FD].fd, MBOX_BMC_EVENT, SEEK_SET); 91 if (rc != MBOX_BMC_EVENT) { 92 MSG_ERR("Couldn't lseek mbox to byte %d: %s\n", MBOX_BMC_EVENT, 93 strerror(errno)); 94 return -MBOX_R_SYSTEM_ERROR; 95 } 96 97 /* Write to mbox status register */ 98 rc = write(context->fds[MBOX_FD].fd, &context->bmc_events, 1); 99 if (rc != 1) { 100 MSG_ERR("Couldn't write to BMC status reg: %s\n", 101 strerror(errno)); 102 return -MBOX_R_SYSTEM_ERROR; 103 } 104 105 /* Reset to start */ 106 rc = lseek(context->fds[MBOX_FD].fd, 0, SEEK_SET); 107 if (rc) { 108 MSG_ERR("Couldn't reset MBOX offset to zero: %s\n", 109 strerror(errno)); 110 return -MBOX_R_SYSTEM_ERROR; 111 } 112 113 return 0; 114 } 115 116 /* 117 * set_bmc_events() - Set BMC events 118 * @context: The mbox context pointer 119 * @bmc_event: The bits to set 120 * @write_back: Whether to write back to the register -> will interrupt host 121 * 122 * Return: 0 on success otherwise negative error code 123 */ 124 int set_bmc_events(struct mbox_context *context, uint8_t bmc_event, 125 bool write_back) 126 { 127 uint8_t mask = 0x00; 128 129 switch (context->version) { 130 case API_VERSION_1: 131 mask = BMC_EVENT_V1_MASK; 132 break; 133 default: 134 mask = BMC_EVENT_V2_MASK; 135 break; 136 } 137 138 context->bmc_events |= (bmc_event & mask); 139 MSG_DBG("BMC Events set to: 0x%.2x\n", context->bmc_events); 140 141 return write_back ? write_bmc_event_reg(context) : 0; 142 } 143 144 /* 145 * clr_bmc_events() - Clear BMC events 146 * @context: The mbox context pointer 147 * @bmc_event: The bits to clear 148 * @write_back: Whether to write back to the register -> will interrupt host 149 * 150 * Return: 0 on success otherwise negative error code 151 */ 152 int clr_bmc_events(struct mbox_context *context, uint8_t bmc_event, 153 bool write_back) 154 { 155 context->bmc_events &= ~bmc_event; 156 MSG_DBG("BMC Events clear to: 0x%.2x\n", context->bmc_events); 157 158 return write_back ? write_bmc_event_reg(context) : 0; 159 } 160 161 /* Command Handlers */ 162 163 /* 164 * Command: RESET_STATE 165 * Reset the LPC mapping to point back at the flash, or memory in case we're 166 * using a virtual pnor. 167 */ 168 int mbox_handle_reset(struct mbox_context *context, 169 union mbox_regs *req, struct mbox_msg *resp) 170 { 171 /* Host requested it -> No BMC Event */ 172 windows_reset_all(context, NO_BMC_EVENT); 173 return lpc_reset(context); 174 } 175 176 /* 177 * Command: GET_MBOX_INFO 178 * Get the API version, default window size and block size 179 * We also set the LPC mapping to point to the reserved memory region here so 180 * this command must be called before any window manipulation 181 * 182 * V1: 183 * ARGS[0]: API Version 184 * 185 * RESP[0]: API Version 186 * RESP[1:2]: Default read window size (number of blocks) 187 * RESP[3:4]: Default write window size (number of blocks) 188 * RESP[5]: Block size (as shift) 189 * 190 * V2: 191 * ARGS[0]: API Version 192 * 193 * RESP[0]: API Version 194 * RESP[1:2]: Default read window size (number of blocks) 195 * RESP[3:4]: Default write window size (number of blocks) 196 * RESP[5]: Block size (as shift) 197 */ 198 int mbox_handle_mbox_info(struct mbox_context *context, 199 union mbox_regs *req, struct mbox_msg *resp) 200 { 201 uint8_t mbox_api_version = req->msg.args[0]; 202 struct protocol_get_info io = { 203 .req = { .api_version = mbox_api_version } 204 }; 205 int rc; 206 207 rc = context->protocol->get_info(context, &io); 208 if (rc < 0) { 209 return mbox_xlate_errno(context, rc); 210 } 211 212 resp->args[0] = io.resp.api_version; 213 if (io.resp.api_version == API_VERSION_1) { 214 put_u16(&resp->args[1], io.resp.v1.read_window_size); 215 put_u16(&resp->args[3], io.resp.v1.write_window_size); 216 } else if (io.resp.api_version >= API_VERSION_2) { 217 resp->args[5] = io.resp.v2.block_size_shift; 218 put_u16(&resp->args[6], io.resp.v2.timeout); 219 } 220 221 return 0; 222 } 223 224 /* 225 * Command: GET_FLASH_INFO 226 * Get the flash size and erase granularity 227 * 228 * V1: 229 * RESP[0:3]: Flash Size (bytes) 230 * RESP[4:7]: Erase Size (bytes) 231 * V2: 232 * RESP[0:1]: Flash Size (number of blocks) 233 * RESP[2:3]: Erase Size (number of blocks) 234 */ 235 int mbox_handle_flash_info(struct mbox_context *context, 236 union mbox_regs *req, struct mbox_msg *resp) 237 { 238 switch (context->version) { 239 case API_VERSION_1: 240 /* Both Sizes in Bytes */ 241 put_u32(&resp->args[0], context->flash_size); 242 put_u32(&resp->args[4], context->mtd_info.erasesize); 243 break; 244 case API_VERSION_2: 245 /* Both Sizes in Block Size */ 246 put_u16(&resp->args[0], 247 context->flash_size >> context->block_size_shift); 248 put_u16(&resp->args[2], 249 context->mtd_info.erasesize >> 250 context->block_size_shift); 251 break; 252 default: 253 MSG_ERR("API Version Not Valid - Invalid System State\n"); 254 return -MBOX_R_SYSTEM_ERROR; 255 } 256 257 return 0; 258 } 259 260 /* 261 * get_lpc_addr_shifted() - Get lpc address of the current window 262 * @context: The mbox context pointer 263 * 264 * Return: The lpc address to access that offset shifted by block size 265 */ 266 static inline uint16_t get_lpc_addr_shifted(struct mbox_context *context) 267 { 268 uint32_t lpc_addr, mem_offset; 269 270 /* Offset of the current window in the reserved memory region */ 271 mem_offset = context->current->mem - context->mem; 272 /* Total LPC Address */ 273 lpc_addr = context->lpc_base + mem_offset; 274 275 MSG_DBG("LPC address of current window: 0x%.8x\n", lpc_addr); 276 277 return lpc_addr >> context->block_size_shift; 278 } 279 280 /* 281 * Command: CREATE_READ_WINDOW 282 * Opens a read window 283 * First checks if any current window with the requested data, if so we just 284 * point the host to that. Otherwise we read the request data in from flash and 285 * point the host there. 286 * 287 * V1: 288 * ARGS[0:1]: Window Location as Offset into Flash (number of blocks) 289 * 290 * RESP[0:1]: LPC bus address for host to access this window (number of blocks) 291 * 292 * V2: 293 * ARGS[0:1]: Window Location as Offset into Flash (number of blocks) 294 * ARGS[2:3]: Requested window size (number of blocks) 295 * 296 * RESP[0:1]: LPC bus address for host to access this window (number of blocks) 297 * RESP[2:3]: Actual window size that the host can access (number of blocks) 298 */ 299 int mbox_handle_read_window(struct mbox_context *context, 300 union mbox_regs *req, struct mbox_msg *resp) 301 { 302 uint32_t flash_offset; 303 int rc; 304 305 /* Close the current window if there is one */ 306 if (context->current) { 307 /* There is an implicit flush if it was a write window */ 308 if (context->current_is_write) { 309 rc = mbox_handle_flush_window(context, NULL, NULL); 310 if (rc < 0) { 311 MSG_ERR("Couldn't Flush Write Window\n"); 312 return rc; 313 } 314 } 315 windows_close_current(context, NO_BMC_EVENT, FLAGS_NONE); 316 } 317 318 /* Offset the host has requested */ 319 flash_offset = get_u16(&req->msg.args[0]) << context->block_size_shift; 320 MSG_INFO("Host requested flash @ 0x%.8x\n", flash_offset); 321 /* Check if we have an existing window */ 322 context->current = windows_search(context, flash_offset, 323 context->version == API_VERSION_1); 324 325 if (!context->current) { /* No existing window */ 326 MSG_DBG("No existing window which maps that flash offset\n"); 327 rc = windows_create_map(context, &context->current, flash_offset, 328 context->version == API_VERSION_1); 329 if (rc < 0) { /* Unable to map offset */ 330 MSG_ERR("Couldn't create window mapping for offset 0x%.8x\n" 331 , flash_offset); 332 return rc; 333 } 334 } 335 336 MSG_INFO("Window @ %p for size 0x%.8x maps flash offset 0x%.8x\n", 337 context->current->mem, context->current->size, 338 context->current->flash_offset); 339 340 put_u16(&resp->args[0], get_lpc_addr_shifted(context)); 341 if (context->version >= API_VERSION_2) { 342 put_u16(&resp->args[2], context->current->size >> 343 context->block_size_shift); 344 put_u16(&resp->args[4], context->current->flash_offset >> 345 context->block_size_shift); 346 } 347 348 context->current_is_write = false; 349 350 return 0; 351 } 352 353 /* 354 * Command: CREATE_WRITE_WINDOW 355 * Opens a write window 356 * First checks if any current window with the requested data, if so we just 357 * point the host to that. Otherwise we read the request data in from flash and 358 * point the host there. 359 * 360 * V1: 361 * ARGS[0:1]: Window Location as Offset into Flash (number of blocks) 362 * 363 * RESP[0:1]: LPC bus address for host to access this window (number of blocks) 364 * 365 * V2: 366 * ARGS[0:1]: Window Location as Offset into Flash (number of blocks) 367 * ARGS[2:3]: Requested window size (number of blocks) 368 * 369 * RESP[0:1]: LPC bus address for host to access this window (number of blocks) 370 * RESP[2:3]: Actual window size that was mapped/host can access (n.o. blocks) 371 */ 372 int mbox_handle_write_window(struct mbox_context *context, 373 union mbox_regs *req, struct mbox_msg *resp) 374 { 375 int rc; 376 377 /* 378 * This is very similar to opening a read window (exactly the same 379 * for now infact) 380 */ 381 rc = mbox_handle_read_window(context, req, resp); 382 if (rc < 0) { 383 return rc; 384 } 385 386 context->current_is_write = true; 387 return rc; 388 } 389 390 /* 391 * Commands: MARK_WRITE_DIRTY 392 * Marks a portion of the current (write) window dirty, informing the daemon 393 * that is has been written to and thus must be at some point written to the 394 * backing store 395 * These changes aren't written back to the backing store unless flush is then 396 * called or the window closed 397 * 398 * V1: 399 * ARGS[0:1]: Where within flash to start (number of blocks) 400 * ARGS[2:5]: Number to mark dirty (number of bytes) 401 * 402 * V2: 403 * ARGS[0:1]: Where within window to start (number of blocks) 404 * ARGS[2:3]: Number to mark dirty (number of blocks) 405 */ 406 int mbox_handle_dirty_window(struct mbox_context *context, 407 union mbox_regs *req, struct mbox_msg *resp) 408 { 409 uint32_t offset, size; 410 int rc; 411 412 if (!(context->current && context->current_is_write)) { 413 MSG_ERR("Tried to call mark dirty without open write window\n"); 414 return context->version >= API_VERSION_2 ? -MBOX_R_WINDOW_ERROR 415 : -MBOX_R_PARAM_ERROR; 416 } 417 418 offset = get_u16(&req->msg.args[0]); 419 420 if (context->version >= API_VERSION_2) { 421 size = get_u16(&req->msg.args[2]); 422 } else { 423 uint32_t off; 424 /* For V1 offset given relative to flash - we want the window */ 425 off = offset - ((context->current->flash_offset) >> 426 context->block_size_shift); 427 if (off > offset) { /* Underflow - before current window */ 428 MSG_ERR("Tried to mark dirty before start of window\n"); 429 MSG_ERR("requested offset: 0x%x window start: 0x%x\n", 430 offset << context->block_size_shift, 431 context->current->flash_offset); 432 return -MBOX_R_PARAM_ERROR; 433 } 434 offset = off; 435 size = get_u32(&req->msg.args[2]); 436 /* 437 * We only track dirty at the block level. 438 * For protocol V1 we can get away with just marking the whole 439 * block dirty. 440 */ 441 size = align_up(size, 1 << context->block_size_shift); 442 size >>= context->block_size_shift; 443 } 444 445 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n", 446 offset << context->block_size_shift, 447 size << context->block_size_shift); 448 449 rc = window_set_bytemap(context, context->current, offset, size, 450 WINDOW_DIRTY); 451 if (rc < 0) { 452 return (rc == -EACCES) ? -MBOX_R_PARAM_ERROR 453 : -MBOX_R_SYSTEM_ERROR; 454 } 455 456 return rc; 457 } 458 459 /* 460 * Commands: MARK_WRITE_ERASE 461 * Erases a portion of the current window 462 * These changes aren't written back to the backing store unless flush is then 463 * called or the window closed 464 * 465 * V1: 466 * Unimplemented 467 * 468 * V2: 469 * ARGS[0:1]: Where within window to start (number of blocks) 470 * ARGS[2:3]: Number to erase (number of blocks) 471 */ 472 int mbox_handle_erase_window(struct mbox_context *context, 473 union mbox_regs *req, struct mbox_msg *resp) 474 { 475 uint32_t offset, size; 476 int rc; 477 478 if (context->version < API_VERSION_2) { 479 MSG_ERR("Protocol Version invalid for Erase Command\n"); 480 return -MBOX_R_PARAM_ERROR; 481 } 482 483 if (!(context->current && context->current_is_write)) { 484 MSG_ERR("Tried to call erase without open write window\n"); 485 return -MBOX_R_WINDOW_ERROR; 486 } 487 488 offset = get_u16(&req->msg.args[0]); 489 size = get_u16(&req->msg.args[2]); 490 491 MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n", 492 offset << context->block_size_shift, 493 size << context->block_size_shift); 494 495 rc = window_set_bytemap(context, context->current, offset, size, 496 WINDOW_ERASED); 497 if (rc < 0) { 498 return (rc == -EACCES) ? -MBOX_R_PARAM_ERROR 499 : -MBOX_R_SYSTEM_ERROR; 500 } 501 502 /* Write 0xFF to mem -> This ensures consistency between flash & ram */ 503 memset(context->current->mem + (offset << context->block_size_shift), 504 0xFF, size << context->block_size_shift); 505 506 return 0; 507 } 508 509 /* 510 * Command: WRITE_FLUSH 511 * Flushes any dirty or erased blocks in the current window back to the backing 512 * store 513 * NOTE: For V1 this behaves much the same as the dirty command in that it 514 * takes an offset and number of blocks to dirty, then also performs a flush as 515 * part of the same command. For V2 this will only flush blocks already marked 516 * dirty/erased with the appropriate commands and doesn't take any arguments 517 * directly. 518 * 519 * V1: 520 * ARGS[0:1]: Where within window to start (number of blocks) 521 * ARGS[2:5]: Number to mark dirty (number of bytes) 522 * 523 * V2: 524 * NONE 525 */ 526 int mbox_handle_flush_window(struct mbox_context *context, 527 union mbox_regs *req, struct mbox_msg *resp) 528 { 529 int rc, i, offset, count; 530 uint8_t prev; 531 532 if (!(context->current && context->current_is_write)) { 533 MSG_ERR("Tried to call flush without open write window\n"); 534 return context->version >= API_VERSION_2 ? -MBOX_R_WINDOW_ERROR 535 : -MBOX_R_PARAM_ERROR; 536 } 537 538 /* 539 * For V1 the Flush command acts much the same as the dirty command 540 * except with a flush as well. Only do this on an actual flush 541 * command not when we call flush because we've implicitly closed a 542 * window because we might not have the required args in req. 543 */ 544 if (context->version == API_VERSION_1 && req && 545 req->msg.command == MBOX_C_WRITE_FLUSH) { 546 rc = mbox_handle_dirty_window(context, req, NULL); 547 if (rc < 0) { 548 return rc; 549 } 550 } 551 552 offset = 0; 553 count = 0; 554 prev = WINDOW_CLEAN; 555 556 MSG_INFO("Flush window @ %p for size 0x%.8x which maps flash @ 0x%.8x\n", 557 context->current->mem, context->current->size, 558 context->current->flash_offset); 559 560 /* 561 * We look for streaks of the same type and keep a count, when the type 562 * (dirty/erased) changes we perform the required action on the backing 563 * store and update the current streak-type 564 */ 565 for (i = 0; i < (context->current->size >> context->block_size_shift); 566 i++) { 567 uint8_t cur = context->current->dirty_bmap[i]; 568 if (cur != WINDOW_CLEAN) { 569 if (cur == prev) { /* Same as previous block, incrmnt */ 570 count++; 571 } else if (prev == WINDOW_CLEAN) { /* Start of run */ 572 offset = i; 573 count++; 574 } else { /* Change in streak type */ 575 rc = window_flush(context, offset, count, 576 prev); 577 if (rc < 0) { 578 return rc; 579 } 580 offset = i; 581 count = 1; 582 } 583 } else { 584 if (prev != WINDOW_CLEAN) { /* End of a streak */ 585 rc = window_flush(context, offset, count, 586 prev); 587 if (rc < 0) { 588 return rc; 589 } 590 offset = 0; 591 count = 0; 592 } 593 } 594 prev = cur; 595 } 596 597 if (prev != WINDOW_CLEAN) { /* Still the last streak to write */ 598 rc = window_flush(context, offset, count, prev); 599 if (rc < 0) { 600 return rc; 601 } 602 } 603 604 /* Clear the dirty bytemap since we have written back all changes */ 605 rc = window_set_bytemap(context, context->current, 0, 606 context->current->size >> 607 context->block_size_shift, 608 WINDOW_CLEAN); 609 if (rc < 0) { 610 return (rc == -EACCES) ? -MBOX_R_PARAM_ERROR 611 : -MBOX_R_SYSTEM_ERROR; 612 } 613 614 return rc; 615 } 616 617 /* 618 * Command: CLOSE_WINDOW 619 * Close the current window 620 * NOTE: There is an implicit flush 621 * 622 * V1: 623 * NONE 624 * 625 * V2: 626 * ARGS[0]: FLAGS 627 */ 628 int mbox_handle_close_window(struct mbox_context *context, 629 union mbox_regs *req, struct mbox_msg *resp) 630 { 631 uint8_t flags = 0; 632 int rc; 633 634 /* Close the current window if there is one */ 635 if (context->current) { 636 /* There is an implicit flush if it was a write window */ 637 if (context->current_is_write) { 638 rc = mbox_handle_flush_window(context, NULL, NULL); 639 if (rc < 0) { 640 MSG_ERR("Couldn't Flush Write Window\n"); 641 return rc; 642 } 643 } 644 645 if (context->version >= API_VERSION_2) { 646 flags = req->msg.args[0]; 647 } 648 649 /* Host asked for it -> Don't set the BMC Event */ 650 windows_close_current(context, NO_BMC_EVENT, flags); 651 } 652 653 return 0; 654 } 655 656 /* 657 * Command: BMC_EVENT_ACK 658 * Sent by the host to acknowledge BMC events supplied in mailbox register 15 659 * 660 * ARGS[0]: Bitmap of bits to ack (by clearing) 661 */ 662 int mbox_handle_ack(struct mbox_context *context, union mbox_regs *req, 663 struct mbox_msg *resp) 664 { 665 uint8_t bmc_events = req->msg.args[0]; 666 667 return clr_bmc_events(context, (bmc_events & BMC_EVENT_ACK_MASK), 668 SET_BMC_EVENT); 669 } 670 671 /* 672 * check_req_valid() - Check if the given request is a valid mbox request 673 * @context: The mbox context pointer 674 * @cmd: The request registers 675 * 676 * Return: 0 if request is valid otherwise negative error code 677 */ 678 static int check_req_valid(struct mbox_context *context, union mbox_regs *req) 679 { 680 uint8_t cmd = req->msg.command; 681 uint8_t seq = req->msg.seq; 682 683 if (cmd > NUM_MBOX_CMDS) { 684 MSG_ERR("Unknown mbox command: %d\n", cmd); 685 return -MBOX_R_PARAM_ERROR; 686 } 687 688 if (seq == context->prev_seq && cmd != MBOX_C_GET_MBOX_INFO) { 689 MSG_ERR("Invalid sequence number: %d, previous: %d\n", seq, 690 context->prev_seq); 691 return -MBOX_R_SEQ_ERROR; 692 } 693 694 if (context->state & STATE_SUSPENDED) { 695 if (cmd != MBOX_C_GET_MBOX_INFO && cmd != MBOX_C_ACK) { 696 MSG_ERR("Cannot use that cmd while suspended: %d\n", 697 cmd); 698 return context->version >= API_VERSION_2 ? -MBOX_R_BUSY 699 : -MBOX_R_PARAM_ERROR; 700 } 701 } 702 703 if (!(context->state & MAPS_MEM)) { 704 if (cmd != MBOX_C_RESET_STATE && cmd != MBOX_C_GET_MBOX_INFO 705 && cmd != MBOX_C_ACK) { 706 MSG_ERR("Must call GET_MBOX_INFO before %d\n", cmd); 707 return -MBOX_R_PARAM_ERROR; 708 } 709 } 710 711 return 0; 712 } 713 714 static const mboxd_mbox_handler mbox_handlers[] = { 715 mbox_handle_reset, 716 mbox_handle_mbox_info, 717 mbox_handle_flash_info, 718 mbox_handle_read_window, 719 mbox_handle_close_window, 720 mbox_handle_write_window, 721 mbox_handle_dirty_window, 722 mbox_handle_flush_window, 723 mbox_handle_ack, 724 mbox_handle_erase_window 725 }; 726 727 /* 728 * handle_mbox_req() - Handle an incoming mbox command request 729 * @context: The mbox context pointer 730 * @req: The mbox request message 731 * 732 * Return: 0 if handled successfully otherwise negative error code 733 */ 734 static int handle_mbox_req(struct mbox_context *context, union mbox_regs *req) 735 { 736 struct mbox_msg resp = { 737 .command = req->msg.command, 738 .seq = req->msg.seq, 739 .args = { 0 }, 740 .response = MBOX_R_SUCCESS 741 }; 742 int rc = 0, len, i; 743 744 MSG_INFO("Received MBOX command: %u\n", req->msg.command); 745 rc = check_req_valid(context, req); 746 if (rc < 0) { 747 resp.response = -rc; 748 } else { 749 /* Commands start at 1 so we have to subtract 1 from the cmd */ 750 mboxd_mbox_handler h = context->handlers[req->msg.command - 1]; 751 rc = h(context, req, &resp); 752 if (rc < 0) { 753 MSG_ERR("Error handling mbox cmd: %d\n", 754 req->msg.command); 755 resp.response = -rc; 756 } 757 } 758 759 context->prev_seq = req->msg.seq; 760 761 MSG_DBG("Writing MBOX response:\n"); 762 MSG_DBG("MBOX cmd: %u\n", resp.command); 763 MSG_DBG("MBOX seq: %u\n", resp.seq); 764 for (i = 0; i < MBOX_ARGS_BYTES; i++) { 765 MSG_DBG("MBOX arg[%d]: 0x%.2x\n", i, resp.args[i]); 766 } 767 MSG_INFO("Writing MBOX response: %u\n", resp.response); 768 len = write(context->fds[MBOX_FD].fd, &resp, sizeof(resp)); 769 if (len < sizeof(resp)) { 770 MSG_ERR("Didn't write the full response\n"); 771 rc = -errno; 772 } 773 774 return rc; 775 } 776 777 /* 778 * get_message() - Read an mbox request message from the mbox registers 779 * @context: The mbox context pointer 780 * @msg: Where to put the received message 781 * 782 * Return: 0 if read successfully otherwise negative error code 783 */ 784 static int get_message(struct mbox_context *context, union mbox_regs *msg) 785 { 786 int rc, i; 787 788 rc = read(context->fds[MBOX_FD].fd, msg, sizeof(msg->raw)); 789 if (rc < 0) { 790 MSG_ERR("Couldn't read: %s\n", strerror(errno)); 791 return -errno; 792 } else if (rc < sizeof(msg->raw)) { 793 MSG_ERR("Short read: %d expecting %zu\n", rc, sizeof(msg->raw)); 794 return -1; 795 } 796 797 MSG_DBG("Received MBOX request:\n"); 798 MSG_DBG("MBOX cmd: %u\n", msg->msg.command); 799 MSG_DBG("MBOX seq: %u\n", msg->msg.seq); 800 for (i = 0; i < MBOX_ARGS_BYTES; i++) { 801 MSG_DBG("MBOX arg[%d]: 0x%.2x\n", i, msg->msg.args[i]); 802 } 803 804 return 0; 805 } 806 807 /* 808 * dispatch_mbox() - handle an mbox interrupt 809 * @context: The mbox context pointer 810 * 811 * Return: 0 if handled successfully otherwise negative error code 812 */ 813 int dispatch_mbox(struct mbox_context *context) 814 { 815 int rc = 0; 816 union mbox_regs req = { 0 }; 817 818 assert(context); 819 820 rc = get_message(context, &req); 821 if (rc) { 822 return rc; 823 } 824 825 return handle_mbox_req(context, &req); 826 } 827 828 int __init_mbox_dev(struct mbox_context *context, const char *path) 829 { 830 int fd; 831 832 context->handlers = mbox_handlers; 833 834 /* Open MBOX Device */ 835 fd = open(path, O_RDWR | O_NONBLOCK); 836 if (fd < 0) { 837 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", 838 path, strerror(errno)); 839 return -errno; 840 } 841 MSG_DBG("Opened mbox dev: %s\n", path); 842 843 context->fds[MBOX_FD].fd = fd; 844 845 return 0; 846 } 847 848 int init_mbox_dev(struct mbox_context *context) 849 { 850 return __init_mbox_dev(context, MBOX_HOST_PATH); 851 } 852 853 void free_mbox_dev(struct mbox_context *context) 854 { 855 close(context->fds[MBOX_FD].fd); 856 } 857