blob: 0cc1cd2be3f9136a511b3b319063de838560d19b [file] [log] [blame]
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +11001/*
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
49static int mbox_handle_flush_window(struct mbox_context *context, union mbox_regs *req,
50 struct mbox_msg *resp);
51
52typedef 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 */
61static 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 */
100int 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
116 return write_back ? write_bmc_event_reg(context) : 0;
117}
118
119/*
120 * clr_bmc_events() - Clear BMC events
121 * @context: The mbox context pointer
122 * @bmc_event: The bits to clear
123 * @write_back: Whether to write back to the register -> will interrupt host
124 *
125 * Return: 0 on success otherwise negative error code
126 */
127int clr_bmc_events(struct mbox_context *context, uint8_t bmc_event,
128 bool write_back)
129{
130 context->bmc_events &= ~bmc_event;
131
132 return write_back ? write_bmc_event_reg(context) : 0;
133}
134
135/* Command Handlers */
136
137/*
138 * Command: RESET_STATE
139 * Reset the LPC mapping to point back at the flash
140 */
141static int mbox_handle_reset(struct mbox_context *context,
142 union mbox_regs *req, struct mbox_msg *resp)
143{
144 /* Host requested it -> No BMC Event */
145 reset_all_windows(context, NO_BMC_EVENT);
146 return point_to_flash(context);
147}
148
149/*
Suraj Jitindar Singh5a3a0662017-04-27 11:55:26 +1000150 * get_suggested_timeout() - get the suggested timeout value in seconds
151 * @context: The mbox context pointer
152 *
153 * Return: Suggested timeout in seconds
154 */
155static uint16_t get_suggested_timeout(struct mbox_context *context)
156{
157 struct window_context *window = find_largest_window(context);
158 uint32_t max_size_mb = window ? (window->size >> 20) : 0;
159
160 return align_up(max_size_mb * FLASH_ACCESS_MS_PER_MB, 1000) / 1000;
161}
162
163/*
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100164 * Command: GET_MBOX_INFO
165 * Get the API version, default window size and block size
166 * We also set the LPC mapping to point to the reserved memory region here so
167 * this command must be called before any window manipulation
168 *
169 * V1:
170 * ARGS[0]: API Version
171 *
172 * RESP[0]: API Version
173 * RESP[1:2]: Default read window size (number of blocks)
174 * RESP[3:4]: Default write window size (number of blocks)
175 * RESP[5]: Block size (as shift)
176 *
177 * V2:
178 * ARGS[0]: API Version
179 *
180 * RESP[0]: API Version
181 * RESP[1:2]: Default read window size (number of blocks)
182 * RESP[3:4]: Default write window size (number of blocks)
183 * RESP[5]: Block size (as shift)
184 */
185static int mbox_handle_mbox_info(struct mbox_context *context,
186 union mbox_regs *req, struct mbox_msg *resp)
187{
188 uint8_t mbox_api_version = req->msg.args[0];
189 uint8_t old_api_version = context->version;
190 int rc;
191
192 /* Check we support the version requested */
Andrew Jefferyfb25aa72017-04-24 11:17:42 +0930193 if (mbox_api_version < API_MIN_VERSION)
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100194 return -MBOX_R_PARAM_ERROR;
Andrew Jefferyfb25aa72017-04-24 11:17:42 +0930195
196 if (mbox_api_version > API_MAX_VERSION)
197 mbox_api_version = API_MAX_VERSION;
198
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100199 context->version = mbox_api_version;
200 MSG_OUT("Using Protocol Version: %d\n", context->version);
201
202 /*
203 * The reset state is currently to have the LPC bus point directly to
204 * flash, since we got a mbox_info command we know the host can talk
205 * mbox so point the LPC bus mapping to the reserved memory region now
206 * so the host can access what we put in it.
207 */
208 rc = point_to_memory(context);
209 if (rc < 0) {
210 return rc;
211 }
212
213 switch (context->version) {
214 case API_VERSION_1:
215 context->block_size_shift = BLOCK_SIZE_SHIFT_V1;
216 break;
217 default:
218 context->block_size_shift = log_2(context->mtd_info.erasesize);
219 break;
220 }
221 MSG_OUT("Block Size Shift: %d\n", context->block_size_shift);
222
223 /* Now we know the blocksize we can allocate the window dirty_bytemap */
224 if (mbox_api_version != old_api_version) {
225 alloc_window_dirty_bytemap(context);
226 }
227 /* Reset if we were V1 since this required exact window mapping */
228 if (old_api_version == API_VERSION_1) {
229 /*
230 * This will only set the BMC event if there was a current
231 * window -> In which case we are better off notifying the
232 * host.
233 */
234 reset_all_windows(context, SET_BMC_EVENT);
235 }
236
237 resp->args[0] = mbox_api_version;
238 if (context->version == API_VERSION_1) {
239 put_u16(&resp->args[1], context->windows.default_size >>
240 context->block_size_shift);
241 put_u16(&resp->args[3], context->windows.default_size >>
242 context->block_size_shift);
243 }
244 if (context->version >= API_VERSION_2) {
245 resp->args[5] = context->block_size_shift;
Suraj Jitindar Singh5a3a0662017-04-27 11:55:26 +1000246 put_u16(&resp->args[6], get_suggested_timeout(context));
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100247 }
248
249 return 0;
250}
251
252/*
253 * Command: GET_FLASH_INFO
254 * Get the flash size and erase granularity
255 *
256 * V1:
257 * RESP[0:3]: Flash Size (bytes)
258 * RESP[4:7]: Erase Size (bytes)
259 * V2:
260 * RESP[0:1]: Flash Size (number of blocks)
261 * RESP[2:3]: Erase Size (number of blocks)
262 */
263static int mbox_handle_flash_info(struct mbox_context *context,
264 union mbox_regs *req, struct mbox_msg *resp)
265{
266 switch (context->version) {
267 case API_VERSION_1:
268 /* Both Sizes in Bytes */
269 put_u32(&resp->args[0], context->flash_size);
270 put_u32(&resp->args[4], context->mtd_info.erasesize);
271 break;
272 case API_VERSION_2:
273 /* Both Sizes in Block Size */
274 put_u16(&resp->args[0],
275 context->flash_size >> context->block_size_shift);
276 put_u16(&resp->args[2],
277 context->mtd_info.erasesize >>
278 context->block_size_shift);
279 break;
280 default:
281 MSG_ERR("API Version Not Valid - Invalid System State\n");
282 return -MBOX_R_SYSTEM_ERROR;
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100283 }
284
285 return 0;
286}
287
288/*
289 * get_lpc_addr_shifted() - Get lpc address of the current window
290 * @context: The mbox context pointer
291 *
292 * Return: The lpc address to access that offset shifted by block size
293 */
294static inline uint16_t get_lpc_addr_shifted(struct mbox_context *context)
295{
296 uint32_t lpc_addr, mem_offset;
297
298 /* Offset of the current window in the reserved memory region */
299 mem_offset = context->current->mem - context->mem;
300 /* Total LPC Address */
301 lpc_addr = context->lpc_base + mem_offset;
302
303 return lpc_addr >> context->block_size_shift;
304}
305
306/*
307 * Command: CREATE_READ_WINDOW
308 * Opens a read window
309 * First checks if any current window with the requested data, if so we just
310 * point the host to that. Otherwise we read the request data in from flash and
311 * point the host there.
312 *
313 * V1:
314 * ARGS[0:1]: Window Location as Offset into Flash (number of blocks)
315 *
316 * RESP[0:1]: LPC bus address for host to access this window (number of blocks)
317 *
318 * V2:
319 * ARGS[0:1]: Window Location as Offset into Flash (number of blocks)
320 * ARGS[2:3]: Requested window size (number of blocks)
321 *
322 * RESP[0:1]: LPC bus address for host to access this window (number of blocks)
323 * RESP[2:3]: Actual window size that the host can access (number of blocks)
324 */
325static int mbox_handle_read_window(struct mbox_context *context,
326 union mbox_regs *req, struct mbox_msg *resp)
327{
328 uint32_t flash_offset;
329 int rc;
330
331 /* Close the current window if there is one */
332 if (context->current) {
333 /* There is an implicit flush if it was a write window */
334 if (context->current_is_write) {
335 rc = mbox_handle_flush_window(context, NULL, NULL);
336 if (rc < 0) {
337 MSG_ERR("Couldn't Flush Write Window\n");
338 return rc;
339 }
340 }
341 close_current_window(context, NO_BMC_EVENT, FLAGS_NONE);
342 }
343
344 /* Offset the host has requested */
345 flash_offset = get_u16(&req->msg.args[0]) << context->block_size_shift;
346 MSG_OUT("Host Requested Flash @ 0x%.8x\n", flash_offset);
347 /* Check if we have an existing window */
348 context->current = search_windows(context, flash_offset,
349 context->version == API_VERSION_1);
350
351 if (!context->current) { /* No existing window */
352 rc = create_map_window(context, &context->current, flash_offset,
353 context->version == API_VERSION_1);
354 if (rc < 0) { /* Unable to map offset */
355 MSG_ERR("Couldn't create window mapping for offset 0x%.8x\n"
356 , flash_offset);
357 return rc;
358 }
359 }
360
361 put_u16(&resp->args[0], get_lpc_addr_shifted(context));
362 if (context->version >= API_VERSION_2) {
363 put_u16(&resp->args[2], context->current->size >>
364 context->block_size_shift);
365 put_u16(&resp->args[4], context->current->flash_offset >>
366 context->block_size_shift);
367 }
368
369 context->current_is_write = false;
370
371 return 0;
372}
373
374/*
375 * Command: CREATE_WRITE_WINDOW
376 * Opens a write window
377 * First checks if any current window with the requested data, if so we just
378 * point the host to that. Otherwise we read the request data in from flash and
379 * point the host there.
380 *
381 * V1:
382 * ARGS[0:1]: Window Location as Offset into Flash (number of blocks)
383 *
384 * RESP[0:1]: LPC bus address for host to access this window (number of blocks)
385 *
386 * V2:
387 * ARGS[0:1]: Window Location as Offset into Flash (number of blocks)
388 * ARGS[2:3]: Requested window size (number of blocks)
389 *
390 * RESP[0:1]: LPC bus address for host to access this window (number of blocks)
391 * RESP[2:3]: Actual window size that was mapped/host can access (n.o. blocks)
392 */
393static int mbox_handle_write_window(struct mbox_context *context,
394 union mbox_regs *req, struct mbox_msg *resp)
395{
396 int rc;
397
398 /*
399 * This is very similar to opening a read window (exactly the same
400 * for now infact)
401 */
402 rc = mbox_handle_read_window(context, req, resp);
403 if (rc < 0) {
404 return rc;
405 }
406
407 context->current_is_write = true;
408 return rc;
409}
410
411/*
412 * Commands: MARK_WRITE_DIRTY
413 * Marks a portion of the current (write) window dirty, informing the daemon
414 * that is has been written to and thus must be at some point written to the
415 * backing store
416 * These changes aren't written back to the backing store unless flush is then
417 * called or the window closed
418 *
419 * V1:
420 * ARGS[0:1]: Where within flash to start (number of blocks)
421 * ARGS[2:5]: Number to mark dirty (number of bytes)
422 *
423 * V2:
424 * ARGS[0:1]: Where within window to start (number of blocks)
425 * ARGS[2:3]: Number to mark dirty (number of blocks)
426 */
427static int mbox_handle_dirty_window(struct mbox_context *context,
428 union mbox_regs *req, struct mbox_msg *resp)
429{
430 uint32_t offset, size;
431
432 if (!(context->current && context->current_is_write)) {
433 MSG_ERR("Tried to call mark dirty without open write window\n");
434 return context->version >= API_VERSION_2 ? -MBOX_R_WINDOW_ERROR
435 : -MBOX_R_PARAM_ERROR;
436 }
437
438 offset = get_u16(&req->msg.args[0]);
439
440 if (context->version >= API_VERSION_2) {
441 size = get_u16(&req->msg.args[2]);
442 } else {
443 uint32_t off;
444 /* For V1 offset given relative to flash - we want the window */
445 off = offset - ((context->current->flash_offset) >>
446 context->block_size_shift);
447 if (off > offset) { /* Underflow - before current window */
448 MSG_ERR("Tried to mark dirty before start of window\n");
449 MSG_ERR("requested offset: 0x%x window start: 0x%x\n",
450 offset << context->block_size_shift,
451 context->current->flash_offset);
452 return -MBOX_R_PARAM_ERROR;
453 }
454 offset = off;
455 size = get_u32(&req->msg.args[2]);
456 /*
457 * We only track dirty at the block level.
458 * For protocol V1 we can get away with just marking the whole
459 * block dirty.
460 */
461 size = align_up(size, 1 << context->block_size_shift);
462 size >>= context->block_size_shift;
463 }
464
465 return set_window_bytemap(context, context->current, offset, size,
466 WINDOW_DIRTY);
467}
468
469/*
470 * Commands: MARK_WRITE_ERASE
471 * Erases a portion of the current window
472 * These changes aren't written back to the backing store unless flush is then
473 * called or the window closed
474 *
475 * V1:
476 * Unimplemented
477 *
478 * V2:
479 * ARGS[0:1]: Where within window to start (number of blocks)
480 * ARGS[2:3]: Number to erase (number of blocks)
481 */
482static int mbox_handle_erase_window(struct mbox_context *context,
483 union mbox_regs *req, struct mbox_msg *resp)
484{
485 uint32_t offset, size;
486 int rc;
487
488 if (context->version < API_VERSION_2) {
489 MSG_ERR("Protocol Version invalid for Erase Command\n");
490 return -MBOX_R_PARAM_ERROR;
491 }
492
493 if (!(context->current && context->current_is_write)) {
494 MSG_ERR("Tried to call erase without open write window\n");
495 return -MBOX_R_WINDOW_ERROR;
496 }
497
498 offset = get_u16(&req->msg.args[0]);
499 size = get_u16(&req->msg.args[2]);
500
501 rc = set_window_bytemap(context, context->current, offset, size,
502 WINDOW_ERASED);
503 if (rc < 0) {
504 return rc;
505 }
506
507 /* Write 0xFF to mem -> This ensures consistency between flash & ram */
508 memset(context->current->mem + (offset << context->block_size_shift),
509 0xFF, size << context->block_size_shift);
510
511 return 0;
512}
513
514/*
515 * Command: WRITE_FLUSH
516 * Flushes any dirty or erased blocks in the current window back to the backing
517 * store
518 * NOTE: For V1 this behaves much the same as the dirty command in that it
519 * takes an offset and number of blocks to dirty, then also performs a flush as
520 * part of the same command. For V2 this will only flush blocks already marked
521 * dirty/erased with the appropriate commands and doesn't take any arguments
522 * directly.
523 *
524 * V1:
525 * ARGS[0:1]: Where within window to start (number of blocks)
526 * ARGS[2:5]: Number to mark dirty (number of bytes)
527 *
528 * V2:
529 * NONE
530 */
531static int mbox_handle_flush_window(struct mbox_context *context,
532 union mbox_regs *req, struct mbox_msg *resp)
533{
534 int rc, i, offset, count;
535 uint8_t prev;
536
537 if (!(context->current && context->current_is_write)) {
538 MSG_ERR("Tried to call flush without open write window\n");
539 return context->version >= API_VERSION_2 ? -MBOX_R_WINDOW_ERROR
540 : -MBOX_R_PARAM_ERROR;
541 }
542
543 /*
544 * For V1 the Flush command acts much the same as the dirty command
545 * except with a flush as well. Only do this on an actual flush
546 * command not when we call flush because we've implicitly closed a
547 * window because we might not have the required args in req.
548 */
549 if (context->version == API_VERSION_1 && req &&
550 req->msg.command == MBOX_C_WRITE_FLUSH) {
551 rc = mbox_handle_dirty_window(context, req, NULL);
552 if (rc < 0) {
553 return rc;
554 }
555 }
556
557 offset = 0;
558 count = 0;
559 prev = WINDOW_CLEAN;
560
561 /*
562 * We look for streaks of the same type and keep a count, when the type
563 * (dirty/erased) changes we perform the required action on the backing
564 * store and update the current streak-type
565 */
566 for (i = 0; i < (context->current->size >> context->block_size_shift);
567 i++) {
568 uint8_t cur = context->current->dirty_bmap[i];
569 if (cur != WINDOW_CLEAN) {
570 if (cur == prev) { /* Same as previous block, incrmnt */
571 count++;
572 } else if (prev == WINDOW_CLEAN) { /* Start of run */
573 offset = i;
574 count++;
575 } else { /* Change in streak type */
576 rc = write_from_window(context, offset, count,
577 prev);
578 if (rc < 0) {
579 return rc;
580 }
581 offset = i;
582 count = 1;
583 }
584 } else {
585 if (prev != WINDOW_CLEAN) { /* End of a streak */
586 rc = write_from_window(context, offset, count,
587 prev);
588 if (rc < 0) {
589 return rc;
590 }
591 offset = 0;
592 count = 0;
593 }
594 }
595 prev = cur;
596 }
597
598 if (prev != WINDOW_CLEAN) { /* Still the last streak to write */
599 rc = write_from_window(context, offset, count, prev);
600 if (rc < 0) {
601 return rc;
602 }
603 }
604
605 /* Clear the dirty bytemap since we have written back all changes */
606 return set_window_bytemap(context, context->current, 0,
607 context->current->size >>
608 context->block_size_shift,
609 WINDOW_CLEAN);
610}
611
612/*
613 * Command: CLOSE_WINDOW
614 * Close the current window
615 * NOTE: There is an implicit flush
616 *
617 * V1:
618 * NONE
619 *
620 * V2:
621 * ARGS[0]: FLAGS
622 */
623static int mbox_handle_close_window(struct mbox_context *context,
624 union mbox_regs *req, struct mbox_msg *resp)
625{
626 uint8_t flags = 0;
627 int rc;
628
629 /* Close the current window if there is one */
630 if (context->current) {
631 /* There is an implicit flush if it was a write window */
632 if (context->current_is_write) {
633 rc = mbox_handle_flush_window(context, NULL, NULL);
634 if (rc < 0) {
635 MSG_ERR("Couldn't Flush Write Window\n");
636 return rc;
637 }
638 }
639
640 if (context->version >= API_VERSION_2) {
641 flags = req->msg.args[0];
642 }
643
644 /* Host asked for it -> Don't set the BMC Event */
645 close_current_window(context, NO_BMC_EVENT, flags);
646 }
647
648 return 0;
649}
650
651/*
652 * Command: BMC_EVENT_ACK
653 * Sent by the host to acknowledge BMC events supplied in mailbox register 15
654 *
655 * ARGS[0]: Bitmap of bits to ack (by clearing)
656 */
657static int mbox_handle_ack(struct mbox_context *context, union mbox_regs *req,
658 struct mbox_msg *resp)
659{
660 uint8_t bmc_events = req->msg.args[0];
661
662 return clr_bmc_events(context, (bmc_events & BMC_EVENT_ACK_MASK),
663 SET_BMC_EVENT);
664}
665
666/*
Andrew Jeffery55dede62017-04-24 16:13:06 +0930667 * check_req_valid() - Check if the given request is a valid mbox request
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100668 * @context: The mbox context pointer
Andrew Jeffery55dede62017-04-24 16:13:06 +0930669 * @cmd: The request registers
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100670 *
Andrew Jeffery55dede62017-04-24 16:13:06 +0930671 * Return: 0 if request is valid otherwise negative error code
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100672 */
Andrew Jeffery55dede62017-04-24 16:13:06 +0930673static int check_req_valid(struct mbox_context *context, union mbox_regs *req)
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100674{
Andrew Jeffery55dede62017-04-24 16:13:06 +0930675 uint8_t cmd = req->msg.command;
676 uint8_t seq = req->msg.seq;
677
678 if (cmd > NUM_MBOX_CMDS) {
Andrew Jeffery121dc0d2017-04-24 16:15:06 +0930679 MSG_ERR("Unknown mbox command: %d\n", cmd);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100680 return -MBOX_R_PARAM_ERROR;
681 }
Andrew Jeffery121dc0d2017-04-24 16:15:06 +0930682
Andrew Jeffery55dede62017-04-24 16:13:06 +0930683 if (seq == context->prev_seq && cmd != MBOX_C_GET_MBOX_INFO) {
684 MSG_ERR("Invalid sequence number: %d\n", seq);
685 return -MBOX_R_SEQ_ERROR;
686 }
687
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100688 if (context->state & STATE_SUSPENDED) {
689 if (cmd != MBOX_C_GET_MBOX_INFO && cmd != MBOX_C_ACK) {
690 MSG_ERR("Cannot use that cmd while suspended: %d\n",
691 cmd);
692 return context->version >= API_VERSION_2 ? -MBOX_R_BUSY
693 : -MBOX_R_PARAM_ERROR;
694 }
695 }
Andrew Jeffery121dc0d2017-04-24 16:15:06 +0930696
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100697 if (!(context->state & MAPS_MEM)) {
698 if (cmd != MBOX_C_RESET_STATE && cmd != MBOX_C_GET_MBOX_INFO
699 && cmd != MBOX_C_ACK) {
Andrew Jeffery121dc0d2017-04-24 16:15:06 +0930700 MSG_ERR("Must call GET_MBOX_INFO before %d\n", cmd);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100701 return -MBOX_R_PARAM_ERROR;
702 }
703 }
704
705 return 0;
706}
707
708static const mboxd_mbox_handler mbox_handlers[] = {
709 mbox_handle_reset,
710 mbox_handle_mbox_info,
711 mbox_handle_flash_info,
712 mbox_handle_read_window,
713 mbox_handle_close_window,
714 mbox_handle_write_window,
715 mbox_handle_dirty_window,
716 mbox_handle_flush_window,
717 mbox_handle_ack,
718 mbox_handle_erase_window
719};
720
721/*
722 * handle_mbox_req() - Handle an incoming mbox command request
723 * @context: The mbox context pointer
724 * @req: The mbox request message
725 *
726 * Return: 0 if handled successfully otherwise negative error code
727 */
728static int handle_mbox_req(struct mbox_context *context, union mbox_regs *req)
729{
730 struct mbox_msg resp = {
731 .command = req->msg.command,
732 .seq = req->msg.seq,
733 .args = { 0 },
734 .response = MBOX_R_SUCCESS
735 };
736 int rc = 0, len;
737
738 MSG_OUT("Got data in with command %d\n", req->msg.command);
Andrew Jeffery55dede62017-04-24 16:13:06 +0930739 rc = check_req_valid(context, req);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100740 if (rc < 0) {
741 resp.response = -rc;
742 } else {
743 /* Commands start at 1 so we have to subtract 1 from the cmd */
744 rc = mbox_handlers[req->msg.command - 1](context, req, &resp);
745 if (rc < 0) {
746 MSG_ERR("Error handling mbox cmd: %d\n",
747 req->msg.command);
748 resp.response = -rc;
749 }
750 }
751
Andrew Jeffery55dede62017-04-24 16:13:06 +0930752 context->prev_seq = req->msg.seq;
753
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100754 MSG_OUT("Writing response to MBOX regs: %d\n", resp.response);
755 len = write(context->fds[MBOX_FD].fd, &resp, sizeof(resp));
756 if (len < sizeof(resp)) {
757 MSG_ERR("Didn't write the full response\n");
758 rc = -errno;
759 }
760
761 return rc;
762}
763
764/*
765 * get_message() - Read an mbox request message from the mbox registers
766 * @context: The mbox context pointer
767 * @msg: Where to put the received message
768 *
769 * Return: 0 if read successfully otherwise negative error code
770 */
771static int get_message(struct mbox_context *context, union mbox_regs *msg)
772{
773 int rc;
774
775 rc = read(context->fds[MBOX_FD].fd, msg, sizeof(msg->raw));
776 if (rc < 0) {
777 MSG_ERR("Couldn't read: %s\n", strerror(errno));
778 return -errno;
779 } else if (rc < sizeof(msg->raw)) {
780 MSG_ERR("Short read: %d expecting %zu\n", rc, sizeof(msg->raw));
781 return -1;
782 }
783
784 return 0;
785}
786
787/*
788 * dispatch_mbox() - handle an mbox interrupt
789 * @context: The mbox context pointer
790 *
791 * Return: 0 if handled successfully otherwise negative error code
792 */
793int dispatch_mbox(struct mbox_context *context)
794{
795 int rc = 0;
796 union mbox_regs req = { 0 };
797
798 assert(context);
799
800 MSG_OUT("Dispatched to mbox\n");
801 rc = get_message(context, &req);
802 if (rc) {
803 return rc;
804 }
805
806 return handle_mbox_req(context, &req);
807}
808
Andrew Jeffery913740f2017-04-10 23:51:07 +0930809int __init_mbox_dev(struct mbox_context *context, const char *path)
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100810{
811 int fd;
812
813 /* Open MBOX Device */
Andrew Jeffery913740f2017-04-10 23:51:07 +0930814 fd = open(path, O_RDWR | O_NONBLOCK);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100815 if (fd < 0) {
816 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n",
Andrew Jeffery913740f2017-04-10 23:51:07 +0930817 path, strerror(errno));
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100818 return -errno;
819 }
820
821 context->fds[MBOX_FD].fd = fd;
822
823 return 0;
824}
825
Andrew Jeffery913740f2017-04-10 23:51:07 +0930826int init_mbox_dev(struct mbox_context *context)
827{
828 return __init_mbox_dev(context, MBOX_HOST_PATH);
829}
830
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100831void free_mbox_dev(struct mbox_context *context)
832{
833 close(context->fds[MBOX_FD].fd);
834}