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