blob: 46679b814ecee1e3675839fcf4f734d853a7469f [file] [log] [blame]
Andrew Jeffery4fe996c2018-02-27 12:16:48 +10301// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2018 IBM Corp.
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +11003
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
33static int mbox_handle_flush_window(struct mbox_context *context, union mbox_regs *req,
34 struct mbox_msg *resp);
35
36typedef 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 */
45static 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 */
84int 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);
Suraj Jitindar Singh28519592017-04-27 14:48:58 +100099 MSG_DBG("BMC Events set to: 0x%.2x\n", context->bmc_events);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100100
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 */
112int clr_bmc_events(struct mbox_context *context, uint8_t bmc_event,
113 bool write_back)
114{
115 context->bmc_events &= ~bmc_event;
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000116 MSG_DBG("BMC Events clear to: 0x%.2x\n", context->bmc_events);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100117
118 return write_back ? write_bmc_event_reg(context) : 0;
119}
120
121/* Command Handlers */
122
123/*
124 * Command: RESET_STATE
Deepak Kodihalli017e45c2017-07-12 01:06:30 -0500125 * Reset the LPC mapping to point back at the flash, or memory in case we're
126 * using a virtual pnor.
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100127 */
128static 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);
Deepak Kodihalli017e45c2017-07-12 01:06:30 -0500133 return reset_lpc(context);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100134}
135
136/*
Suraj Jitindar Singh5a3a0662017-04-27 11:55:26 +1000137 * get_suggested_timeout() - get the suggested timeout value in seconds
138 * @context: The mbox context pointer
139 *
140 * Return: Suggested timeout in seconds
141 */
142static 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;
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000146 uint8_t ret;
Suraj Jitindar Singh5a3a0662017-04-27 11:55:26 +1000147
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000148 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;
Suraj Jitindar Singh5a3a0662017-04-27 11:55:26 +1000153}
154
155/*
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100156 * 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 */
177static 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 */
Andrew Jefferyfb25aa72017-04-24 11:17:42 +0930185 if (mbox_api_version < API_MIN_VERSION)
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100186 return -MBOX_R_PARAM_ERROR;
Andrew Jefferyfb25aa72017-04-24 11:17:42 +0930187
188 if (mbox_api_version > API_MAX_VERSION)
189 mbox_api_version = API_MAX_VERSION;
190
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100191 context->version = mbox_api_version;
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000192 MSG_INFO("Using Protocol Version: %d\n", context->version);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100193
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 }
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000213 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
214 1 << context->block_size_shift, context->block_size_shift);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100215
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;
Suraj Jitindar Singh5a3a0662017-04-27 11:55:26 +1000239 put_u16(&resp->args[6], get_suggested_timeout(context));
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100240 }
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 */
256static 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;
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100276 }
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 */
287static 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
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000296 MSG_DBG("LPC address of current window: 0x%.8x\n", lpc_addr);
297
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100298 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 */
320static 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;
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000341 MSG_INFO("Host requested flash @ 0x%.8x\n", flash_offset);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100342 /* 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 */
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000347 MSG_DBG("No existing window which maps that flash offset\n");
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100348 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
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000357 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
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100361 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
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000465 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
466 offset << context->block_size_shift,
467 size << context->block_size_shift);
468
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100469 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 */
486static 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
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000505 MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n",
506 offset << context->block_size_shift,
507 size << context->block_size_shift);
508
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100509 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 */
539static 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
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000569 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
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100573 /*
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 */
635static 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 */
669static 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/*
Andrew Jeffery55dede62017-04-24 16:13:06 +0930679 * check_req_valid() - Check if the given request is a valid mbox request
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100680 * @context: The mbox context pointer
Andrew Jeffery55dede62017-04-24 16:13:06 +0930681 * @cmd: The request registers
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100682 *
Andrew Jeffery55dede62017-04-24 16:13:06 +0930683 * Return: 0 if request is valid otherwise negative error code
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100684 */
Andrew Jeffery55dede62017-04-24 16:13:06 +0930685static int check_req_valid(struct mbox_context *context, union mbox_regs *req)
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100686{
Andrew Jeffery55dede62017-04-24 16:13:06 +0930687 uint8_t cmd = req->msg.command;
688 uint8_t seq = req->msg.seq;
689
690 if (cmd > NUM_MBOX_CMDS) {
Andrew Jeffery121dc0d2017-04-24 16:15:06 +0930691 MSG_ERR("Unknown mbox command: %d\n", cmd);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100692 return -MBOX_R_PARAM_ERROR;
693 }
Andrew Jeffery121dc0d2017-04-24 16:15:06 +0930694
Andrew Jeffery55dede62017-04-24 16:13:06 +0930695 if (seq == context->prev_seq && cmd != MBOX_C_GET_MBOX_INFO) {
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000696 MSG_ERR("Invalid sequence number: %d, previous: %d\n", seq,
697 context->prev_seq);
Andrew Jeffery55dede62017-04-24 16:13:06 +0930698 return -MBOX_R_SEQ_ERROR;
699 }
700
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100701 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 }
Andrew Jeffery121dc0d2017-04-24 16:15:06 +0930709
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100710 if (!(context->state & MAPS_MEM)) {
711 if (cmd != MBOX_C_RESET_STATE && cmd != MBOX_C_GET_MBOX_INFO
712 && cmd != MBOX_C_ACK) {
Andrew Jeffery121dc0d2017-04-24 16:15:06 +0930713 MSG_ERR("Must call GET_MBOX_INFO before %d\n", cmd);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100714 return -MBOX_R_PARAM_ERROR;
715 }
716 }
717
718 return 0;
719}
720
721static 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 */
741static 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 };
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000749 int rc = 0, len, i;
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100750
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000751 MSG_INFO("Received MBOX command: %u\n", req->msg.command);
Andrew Jeffery55dede62017-04-24 16:13:06 +0930752 rc = check_req_valid(context, req);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100753 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
Andrew Jeffery55dede62017-04-24 16:13:06 +0930765 context->prev_seq = req->msg.seq;
766
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000767 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);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100774 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 */
790static int get_message(struct mbox_context *context, union mbox_regs *msg)
791{
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000792 int rc, i;
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100793
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
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000803 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
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100810 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 */
819int dispatch_mbox(struct mbox_context *context)
820{
821 int rc = 0;
822 union mbox_regs req = { 0 };
823
824 assert(context);
825
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100826 rc = get_message(context, &req);
827 if (rc) {
828 return rc;
829 }
830
831 return handle_mbox_req(context, &req);
832}
833
Andrew Jeffery913740f2017-04-10 23:51:07 +0930834int __init_mbox_dev(struct mbox_context *context, const char *path)
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100835{
836 int fd;
837
838 /* Open MBOX Device */
Andrew Jeffery913740f2017-04-10 23:51:07 +0930839 fd = open(path, O_RDWR | O_NONBLOCK);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100840 if (fd < 0) {
841 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n",
Andrew Jeffery913740f2017-04-10 23:51:07 +0930842 path, strerror(errno));
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100843 return -errno;
844 }
Suraj Jitindar Singh28519592017-04-27 14:48:58 +1000845 MSG_DBG("Opened mbox dev: %s\n", path);
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100846
847 context->fds[MBOX_FD].fd = fd;
848
849 return 0;
850}
851
Andrew Jeffery913740f2017-04-10 23:51:07 +0930852int init_mbox_dev(struct mbox_context *context)
853{
854 return __init_mbox_dev(context, MBOX_HOST_PATH);
855}
856
Suraj Jitindar Singhe39c9162017-03-28 10:47:43 +1100857void free_mbox_dev(struct mbox_context *context)
858{
859 close(context->fds[MBOX_FD].fd);
860}