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 int rc = context->protocol->reset(context); 172 if (rc < 0) { 173 return mbox_xlate_errno(context, rc); 174 } 175 176 return 0; 177 } 178 179 /* 180 * Command: GET_MBOX_INFO 181 * Get the API version, default window size and block size 182 * We also set the LPC mapping to point to the reserved memory region here so 183 * this command must be called before any window manipulation 184 * 185 * V1: 186 * ARGS[0]: API Version 187 * 188 * RESP[0]: API Version 189 * RESP[1:2]: Default read window size (number of blocks) 190 * RESP[3:4]: Default write window size (number of blocks) 191 * RESP[5]: Block size (as shift) 192 * 193 * V2: 194 * ARGS[0]: API Version 195 * 196 * RESP[0]: API Version 197 * RESP[1:2]: Default read window size (number of blocks) 198 * RESP[3:4]: Default write window size (number of blocks) 199 * RESP[5]: Block size (as shift) 200 */ 201 int mbox_handle_mbox_info(struct mbox_context *context, 202 union mbox_regs *req, struct mbox_msg *resp) 203 { 204 uint8_t mbox_api_version = req->msg.args[0]; 205 struct protocol_get_info io = { 206 .req = { .api_version = mbox_api_version } 207 }; 208 int rc; 209 210 rc = context->protocol->get_info(context, &io); 211 if (rc < 0) { 212 return mbox_xlate_errno(context, rc); 213 } 214 215 resp->args[0] = io.resp.api_version; 216 if (io.resp.api_version == API_VERSION_1) { 217 put_u16(&resp->args[1], io.resp.v1.read_window_size); 218 put_u16(&resp->args[3], io.resp.v1.write_window_size); 219 } else if (io.resp.api_version >= API_VERSION_2) { 220 resp->args[5] = io.resp.v2.block_size_shift; 221 put_u16(&resp->args[6], io.resp.v2.timeout); 222 } 223 224 return 0; 225 } 226 227 /* 228 * Command: GET_FLASH_INFO 229 * Get the flash size and erase granularity 230 * 231 * V1: 232 * RESP[0:3]: Flash Size (bytes) 233 * RESP[4:7]: Erase Size (bytes) 234 * V2: 235 * RESP[0:1]: Flash Size (number of blocks) 236 * RESP[2:3]: Erase Size (number of blocks) 237 */ 238 int mbox_handle_flash_info(struct mbox_context *context, 239 union mbox_regs *req, struct mbox_msg *resp) 240 { 241 struct protocol_get_flash_info io; 242 int rc; 243 244 rc = context->protocol->get_flash_info(context, &io); 245 if (rc < 0) { 246 return mbox_xlate_errno(context, rc); 247 } 248 249 switch (context->version) { 250 case API_VERSION_1: 251 /* Both Sizes in Bytes */ 252 put_u32(&resp->args[0], io.resp.v1.flash_size); 253 put_u32(&resp->args[4], io.resp.v1.erase_size); 254 break; 255 case API_VERSION_2: 256 /* Both Sizes in Block Size */ 257 put_u16(&resp->args[0], io.resp.v2.flash_size); 258 put_u16(&resp->args[2], io.resp.v2.erase_size); 259 break; 260 default: 261 MSG_ERR("API Version Not Valid - Invalid System State\n"); 262 return -MBOX_R_SYSTEM_ERROR; 263 } 264 265 return 0; 266 } 267 268 /* 269 * get_lpc_addr_shifted() - Get lpc address of the current window 270 * @context: The mbox context pointer 271 * 272 * Return: The lpc address to access that offset shifted by block size 273 */ 274 static inline uint16_t get_lpc_addr_shifted(struct mbox_context *context) 275 { 276 uint32_t lpc_addr, mem_offset; 277 278 /* Offset of the current window in the reserved memory region */ 279 mem_offset = context->current->mem - context->mem; 280 /* Total LPC Address */ 281 lpc_addr = context->lpc_base + mem_offset; 282 283 MSG_DBG("LPC address of current window: 0x%.8x\n", lpc_addr); 284 285 return lpc_addr >> context->block_size_shift; 286 } 287 288 /* 289 * Command: CREATE_READ_WINDOW 290 * Opens a read window 291 * First checks if any current window with the requested data, if so we just 292 * point the host to that. Otherwise we read the request data in from flash and 293 * point the host there. 294 * 295 * V1: 296 * ARGS[0:1]: Window Location as Offset into Flash (number of blocks) 297 * 298 * RESP[0:1]: LPC bus address for host to access this window (number of blocks) 299 * 300 * V2: 301 * ARGS[0:1]: Window Location as Offset into Flash (number of blocks) 302 * ARGS[2:3]: Requested window size (number of blocks) 303 * 304 * RESP[0:1]: LPC bus address for host to access this window (number of blocks) 305 * RESP[2:3]: Actual window size that the host can access (number of blocks) 306 */ 307 int mbox_handle_read_window(struct mbox_context *context, 308 union mbox_regs *req, struct mbox_msg *resp) 309 { 310 struct protocol_create_window io; 311 int rc; 312 313 io.req.offset = get_u16(&req->msg.args[0]); 314 315 rc = context->protocol->create_read_window(context, &io); 316 if (rc < 0) { 317 return mbox_xlate_errno(context, rc); 318 } 319 320 put_u16(&resp->args[0], io.resp.lpc_address); 321 if (context->version >= API_VERSION_2) { 322 put_u16(&resp->args[2], io.resp.size); 323 put_u16(&resp->args[4], io.resp.offset); 324 } 325 326 context->current_is_write = false; 327 328 return 0; 329 } 330 331 /* 332 * Command: CREATE_WRITE_WINDOW 333 * Opens a write window 334 * First checks if any current window with the requested data, if so we just 335 * point the host to that. Otherwise we read the request data in from flash and 336 * point the host there. 337 * 338 * V1: 339 * ARGS[0:1]: Window Location as Offset into Flash (number of blocks) 340 * 341 * RESP[0:1]: LPC bus address for host to access this window (number of blocks) 342 * 343 * V2: 344 * ARGS[0:1]: Window Location as Offset into Flash (number of blocks) 345 * ARGS[2:3]: Requested window size (number of blocks) 346 * 347 * RESP[0:1]: LPC bus address for host to access this window (number of blocks) 348 * RESP[2:3]: Actual window size that was mapped/host can access (n.o. blocks) 349 */ 350 int mbox_handle_write_window(struct mbox_context *context, 351 union mbox_regs *req, struct mbox_msg *resp) 352 { 353 int rc; 354 355 /* 356 * This is very similar to opening a read window (exactly the same 357 * for now infact) 358 */ 359 rc = mbox_handle_read_window(context, req, resp); 360 if (rc < 0) { 361 return rc; 362 } 363 364 context->current_is_write = true; 365 return rc; 366 } 367 368 /* 369 * Commands: MARK_WRITE_DIRTY 370 * Marks a portion of the current (write) window dirty, informing the daemon 371 * that is has been written to and thus must be at some point written to the 372 * backing store 373 * These changes aren't written back to the backing store unless flush is then 374 * called or the window closed 375 * 376 * V1: 377 * ARGS[0:1]: Where within flash to start (number of blocks) 378 * ARGS[2:5]: Number to mark dirty (number of bytes) 379 * 380 * V2: 381 * ARGS[0:1]: Where within window to start (number of blocks) 382 * ARGS[2:3]: Number to mark dirty (number of blocks) 383 */ 384 int mbox_handle_dirty_window(struct mbox_context *context, 385 union mbox_regs *req, struct mbox_msg *resp) 386 { 387 uint32_t offset, size; 388 int rc; 389 390 if (!(context->current && context->current_is_write)) { 391 MSG_ERR("Tried to call mark dirty without open write window\n"); 392 return context->version >= API_VERSION_2 ? -MBOX_R_WINDOW_ERROR 393 : -MBOX_R_PARAM_ERROR; 394 } 395 396 offset = get_u16(&req->msg.args[0]); 397 398 if (context->version >= API_VERSION_2) { 399 size = get_u16(&req->msg.args[2]); 400 } else { 401 uint32_t off; 402 /* For V1 offset given relative to flash - we want the window */ 403 off = offset - ((context->current->flash_offset) >> 404 context->block_size_shift); 405 if (off > offset) { /* Underflow - before current window */ 406 MSG_ERR("Tried to mark dirty before start of window\n"); 407 MSG_ERR("requested offset: 0x%x window start: 0x%x\n", 408 offset << context->block_size_shift, 409 context->current->flash_offset); 410 return -MBOX_R_PARAM_ERROR; 411 } 412 offset = off; 413 size = get_u32(&req->msg.args[2]); 414 /* 415 * We only track dirty at the block level. 416 * For protocol V1 we can get away with just marking the whole 417 * block dirty. 418 */ 419 size = align_up(size, 1 << context->block_size_shift); 420 size >>= context->block_size_shift; 421 } 422 423 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n", 424 offset << context->block_size_shift, 425 size << context->block_size_shift); 426 427 rc = window_set_bytemap(context, context->current, offset, size, 428 WINDOW_DIRTY); 429 if (rc < 0) { 430 return (rc == -EACCES) ? -MBOX_R_PARAM_ERROR 431 : -MBOX_R_SYSTEM_ERROR; 432 } 433 434 return rc; 435 } 436 437 /* 438 * Commands: MARK_WRITE_ERASE 439 * Erases a portion of the current window 440 * These changes aren't written back to the backing store unless flush is then 441 * called or the window closed 442 * 443 * V1: 444 * Unimplemented 445 * 446 * V2: 447 * ARGS[0:1]: Where within window to start (number of blocks) 448 * ARGS[2:3]: Number to erase (number of blocks) 449 */ 450 int mbox_handle_erase_window(struct mbox_context *context, 451 union mbox_regs *req, struct mbox_msg *resp) 452 { 453 uint32_t offset, size; 454 int rc; 455 456 if (context->version < API_VERSION_2) { 457 MSG_ERR("Protocol Version invalid for Erase Command\n"); 458 return -MBOX_R_PARAM_ERROR; 459 } 460 461 if (!(context->current && context->current_is_write)) { 462 MSG_ERR("Tried to call erase without open write window\n"); 463 return -MBOX_R_WINDOW_ERROR; 464 } 465 466 offset = get_u16(&req->msg.args[0]); 467 size = get_u16(&req->msg.args[2]); 468 469 MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n", 470 offset << context->block_size_shift, 471 size << context->block_size_shift); 472 473 rc = window_set_bytemap(context, context->current, offset, size, 474 WINDOW_ERASED); 475 if (rc < 0) { 476 return (rc == -EACCES) ? -MBOX_R_PARAM_ERROR 477 : -MBOX_R_SYSTEM_ERROR; 478 } 479 480 /* Write 0xFF to mem -> This ensures consistency between flash & ram */ 481 memset(context->current->mem + (offset << context->block_size_shift), 482 0xFF, size << context->block_size_shift); 483 484 return 0; 485 } 486 487 /* 488 * Command: WRITE_FLUSH 489 * Flushes any dirty or erased blocks in the current window back to the backing 490 * store 491 * NOTE: For V1 this behaves much the same as the dirty command in that it 492 * takes an offset and number of blocks to dirty, then also performs a flush as 493 * part of the same command. For V2 this will only flush blocks already marked 494 * dirty/erased with the appropriate commands and doesn't take any arguments 495 * directly. 496 * 497 * V1: 498 * ARGS[0:1]: Where within window to start (number of blocks) 499 * ARGS[2:5]: Number to mark dirty (number of bytes) 500 * 501 * V2: 502 * NONE 503 */ 504 int mbox_handle_flush_window(struct mbox_context *context, 505 union mbox_regs *req, struct mbox_msg *resp) 506 { 507 int rc, i, offset, count; 508 uint8_t prev; 509 510 if (!(context->current && context->current_is_write)) { 511 MSG_ERR("Tried to call flush without open write window\n"); 512 return context->version >= API_VERSION_2 ? -MBOX_R_WINDOW_ERROR 513 : -MBOX_R_PARAM_ERROR; 514 } 515 516 /* 517 * For V1 the Flush command acts much the same as the dirty command 518 * except with a flush as well. Only do this on an actual flush 519 * command not when we call flush because we've implicitly closed a 520 * window because we might not have the required args in req. 521 */ 522 if (context->version == API_VERSION_1 && req && 523 req->msg.command == MBOX_C_WRITE_FLUSH) { 524 rc = mbox_handle_dirty_window(context, req, NULL); 525 if (rc < 0) { 526 return rc; 527 } 528 } 529 530 offset = 0; 531 count = 0; 532 prev = WINDOW_CLEAN; 533 534 MSG_INFO("Flush window @ %p for size 0x%.8x which maps flash @ 0x%.8x\n", 535 context->current->mem, context->current->size, 536 context->current->flash_offset); 537 538 /* 539 * We look for streaks of the same type and keep a count, when the type 540 * (dirty/erased) changes we perform the required action on the backing 541 * store and update the current streak-type 542 */ 543 for (i = 0; i < (context->current->size >> context->block_size_shift); 544 i++) { 545 uint8_t cur = context->current->dirty_bmap[i]; 546 if (cur != WINDOW_CLEAN) { 547 if (cur == prev) { /* Same as previous block, incrmnt */ 548 count++; 549 } else if (prev == WINDOW_CLEAN) { /* Start of run */ 550 offset = i; 551 count++; 552 } else { /* Change in streak type */ 553 rc = window_flush(context, offset, count, 554 prev); 555 if (rc < 0) { 556 return rc; 557 } 558 offset = i; 559 count = 1; 560 } 561 } else { 562 if (prev != WINDOW_CLEAN) { /* End of a streak */ 563 rc = window_flush(context, offset, count, 564 prev); 565 if (rc < 0) { 566 return rc; 567 } 568 offset = 0; 569 count = 0; 570 } 571 } 572 prev = cur; 573 } 574 575 if (prev != WINDOW_CLEAN) { /* Still the last streak to write */ 576 rc = window_flush(context, offset, count, prev); 577 if (rc < 0) { 578 return rc; 579 } 580 } 581 582 /* Clear the dirty bytemap since we have written back all changes */ 583 rc = window_set_bytemap(context, context->current, 0, 584 context->current->size >> 585 context->block_size_shift, 586 WINDOW_CLEAN); 587 if (rc < 0) { 588 return (rc == -EACCES) ? -MBOX_R_PARAM_ERROR 589 : -MBOX_R_SYSTEM_ERROR; 590 } 591 592 return rc; 593 } 594 595 /* 596 * Command: CLOSE_WINDOW 597 * Close the current window 598 * NOTE: There is an implicit flush 599 * 600 * V1: 601 * NONE 602 * 603 * V2: 604 * ARGS[0]: FLAGS 605 */ 606 int mbox_handle_close_window(struct mbox_context *context, 607 union mbox_regs *req, struct mbox_msg *resp) 608 { 609 uint8_t flags = 0; 610 int rc; 611 612 /* Close the current window if there is one */ 613 if (context->current) { 614 /* There is an implicit flush if it was a write window */ 615 if (context->current_is_write) { 616 rc = mbox_handle_flush_window(context, NULL, NULL); 617 if (rc < 0) { 618 MSG_ERR("Couldn't Flush Write Window\n"); 619 return rc; 620 } 621 } 622 623 if (context->version >= API_VERSION_2) { 624 flags = req->msg.args[0]; 625 } 626 627 /* Host asked for it -> Don't set the BMC Event */ 628 windows_close_current(context, NO_BMC_EVENT, flags); 629 } 630 631 return 0; 632 } 633 634 /* 635 * Command: BMC_EVENT_ACK 636 * Sent by the host to acknowledge BMC events supplied in mailbox register 15 637 * 638 * ARGS[0]: Bitmap of bits to ack (by clearing) 639 */ 640 int mbox_handle_ack(struct mbox_context *context, union mbox_regs *req, 641 struct mbox_msg *resp) 642 { 643 uint8_t bmc_events = req->msg.args[0]; 644 645 return clr_bmc_events(context, (bmc_events & BMC_EVENT_ACK_MASK), 646 SET_BMC_EVENT); 647 } 648 649 /* 650 * check_req_valid() - Check if the given request is a valid mbox request 651 * @context: The mbox context pointer 652 * @cmd: The request registers 653 * 654 * Return: 0 if request is valid otherwise negative error code 655 */ 656 static int check_req_valid(struct mbox_context *context, union mbox_regs *req) 657 { 658 uint8_t cmd = req->msg.command; 659 uint8_t seq = req->msg.seq; 660 661 if (cmd > NUM_MBOX_CMDS) { 662 MSG_ERR("Unknown mbox command: %d\n", cmd); 663 return -MBOX_R_PARAM_ERROR; 664 } 665 666 if (seq == context->prev_seq && cmd != MBOX_C_GET_MBOX_INFO) { 667 MSG_ERR("Invalid sequence number: %d, previous: %d\n", seq, 668 context->prev_seq); 669 return -MBOX_R_SEQ_ERROR; 670 } 671 672 if (context->state & STATE_SUSPENDED) { 673 if (cmd != MBOX_C_GET_MBOX_INFO && cmd != MBOX_C_ACK) { 674 MSG_ERR("Cannot use that cmd while suspended: %d\n", 675 cmd); 676 return context->version >= API_VERSION_2 ? -MBOX_R_BUSY 677 : -MBOX_R_PARAM_ERROR; 678 } 679 } 680 681 if (!(context->state & MAPS_MEM)) { 682 if (cmd != MBOX_C_RESET_STATE && cmd != MBOX_C_GET_MBOX_INFO 683 && cmd != MBOX_C_ACK) { 684 MSG_ERR("Must call GET_MBOX_INFO before %d\n", cmd); 685 return -MBOX_R_PARAM_ERROR; 686 } 687 } 688 689 return 0; 690 } 691 692 static const mboxd_mbox_handler mbox_handlers[] = { 693 mbox_handle_reset, 694 mbox_handle_mbox_info, 695 mbox_handle_flash_info, 696 mbox_handle_read_window, 697 mbox_handle_close_window, 698 mbox_handle_write_window, 699 mbox_handle_dirty_window, 700 mbox_handle_flush_window, 701 mbox_handle_ack, 702 mbox_handle_erase_window 703 }; 704 705 /* 706 * handle_mbox_req() - Handle an incoming mbox command request 707 * @context: The mbox context pointer 708 * @req: The mbox request message 709 * 710 * Return: 0 if handled successfully otherwise negative error code 711 */ 712 static int handle_mbox_req(struct mbox_context *context, union mbox_regs *req) 713 { 714 struct mbox_msg resp = { 715 .command = req->msg.command, 716 .seq = req->msg.seq, 717 .args = { 0 }, 718 .response = MBOX_R_SUCCESS 719 }; 720 int rc = 0, len, i; 721 722 MSG_INFO("Received MBOX command: %u\n", req->msg.command); 723 rc = check_req_valid(context, req); 724 if (rc < 0) { 725 resp.response = -rc; 726 } else { 727 /* Commands start at 1 so we have to subtract 1 from the cmd */ 728 mboxd_mbox_handler h = context->handlers[req->msg.command - 1]; 729 rc = h(context, req, &resp); 730 if (rc < 0) { 731 MSG_ERR("Error handling mbox cmd: %d\n", 732 req->msg.command); 733 resp.response = -rc; 734 } 735 } 736 737 context->prev_seq = req->msg.seq; 738 739 MSG_DBG("Writing MBOX response:\n"); 740 MSG_DBG("MBOX cmd: %u\n", resp.command); 741 MSG_DBG("MBOX seq: %u\n", resp.seq); 742 for (i = 0; i < MBOX_ARGS_BYTES; i++) { 743 MSG_DBG("MBOX arg[%d]: 0x%.2x\n", i, resp.args[i]); 744 } 745 MSG_INFO("Writing MBOX response: %u\n", resp.response); 746 len = write(context->fds[MBOX_FD].fd, &resp, sizeof(resp)); 747 if (len < sizeof(resp)) { 748 MSG_ERR("Didn't write the full response\n"); 749 rc = -errno; 750 } 751 752 return rc; 753 } 754 755 /* 756 * get_message() - Read an mbox request message from the mbox registers 757 * @context: The mbox context pointer 758 * @msg: Where to put the received message 759 * 760 * Return: 0 if read successfully otherwise negative error code 761 */ 762 static int get_message(struct mbox_context *context, union mbox_regs *msg) 763 { 764 int rc, i; 765 766 rc = read(context->fds[MBOX_FD].fd, msg, sizeof(msg->raw)); 767 if (rc < 0) { 768 MSG_ERR("Couldn't read: %s\n", strerror(errno)); 769 return -errno; 770 } else if (rc < sizeof(msg->raw)) { 771 MSG_ERR("Short read: %d expecting %zu\n", rc, sizeof(msg->raw)); 772 return -1; 773 } 774 775 MSG_DBG("Received MBOX request:\n"); 776 MSG_DBG("MBOX cmd: %u\n", msg->msg.command); 777 MSG_DBG("MBOX seq: %u\n", msg->msg.seq); 778 for (i = 0; i < MBOX_ARGS_BYTES; i++) { 779 MSG_DBG("MBOX arg[%d]: 0x%.2x\n", i, msg->msg.args[i]); 780 } 781 782 return 0; 783 } 784 785 /* 786 * dispatch_mbox() - handle an mbox interrupt 787 * @context: The mbox context pointer 788 * 789 * Return: 0 if handled successfully otherwise negative error code 790 */ 791 int dispatch_mbox(struct mbox_context *context) 792 { 793 int rc = 0; 794 union mbox_regs req = { 0 }; 795 796 assert(context); 797 798 rc = get_message(context, &req); 799 if (rc) { 800 return rc; 801 } 802 803 return handle_mbox_req(context, &req); 804 } 805 806 int __init_mbox_dev(struct mbox_context *context, const char *path) 807 { 808 int fd; 809 810 context->handlers = mbox_handlers; 811 812 /* Open MBOX Device */ 813 fd = open(path, O_RDWR | O_NONBLOCK); 814 if (fd < 0) { 815 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", 816 path, strerror(errno)); 817 return -errno; 818 } 819 MSG_DBG("Opened mbox dev: %s\n", path); 820 821 context->fds[MBOX_FD].fd = fd; 822 823 return 0; 824 } 825 826 int init_mbox_dev(struct mbox_context *context) 827 { 828 return __init_mbox_dev(context, MBOX_HOST_PATH); 829 } 830 831 void free_mbox_dev(struct mbox_context *context) 832 { 833 close(context->fds[MBOX_FD].fd); 834 } 835