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