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