summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuraj Jitindar Singh <sjitindarsingh@gmail.com>2017-03-28 10:47:43 +1100
committerSuraj Jitindar Singh <sjitindarsingh@gmail.com>2017-04-11 11:41:54 +1000
commite39c91637337fc1afc54fe8e215f1493395601a3 (patch)
tree0af6bb466ed1ca0afcb03e568a3a98da5723c06d
parent2dff340a846068f33a0de171a75d4187bc189ea3 (diff)
downloadphosphor-mboxd-e39c91637337fc1afc54fe8e215f1493395601a3.tar.gz
phosphor-mboxd-e39c91637337fc1afc54fe8e215f1493395601a3.zip
mboxd: Update mboxd to implement protocol V2 and add dbus support
Version 2 of the mbox protocol contains a few changes such as: - All sizes are in block size - Adds an erase command - Adds new response codes - Adds new BMC events - Open windows commands now take a size directive Update the mailbox daemon to support version 2 of the protocol which includes implementing all of the V2 functionality. Also entirely refactor the mboxd.c code to make it more modular improving readability and maintainability. At the same time improve the functionality by adding: - Multiple windows in the daemon (still only one active window) to cache flash contents - Implement a dbus interface to allow interaction with the daemon - Handle sigterm and sigint and terminate cleanly The previous implementation utilised the entire reserved memory region. Update the daemon so that on the command line the number of windows and the size of each which the reserved memory region will be split into can be specified. The reserved memory region is then divided between the windows, however there can still only be one "active" window at a time. The daemon uses these windows to cache the flash contents meaning the flash doesn't have to be copied when the host requests access assuming the daemon already has a copy. A dbus interface is added so that commands can be sent to the daemon to control it's operation from the bmc. These include suspending and resuming the daemon to synchronise flash access, telling the daemon to point the lpc mapping back to flash and telling the daemon when the flash has been modified out from under it. Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com> Change-Id: I10be01a395c2bec437cf2c825fdd144580b60dbc
-rw-r--r--Makefile.am6
-rw-r--r--common.h58
-rw-r--r--configure.ac4
-rw-r--r--dbus.h65
-rw-r--r--mbox.h193
-rw-r--r--mboxd.c760
-rw-r--r--mboxd_dbus.c372
-rw-r--r--mboxd_dbus.h24
-rw-r--r--mboxd_flash.c278
-rw-r--r--mboxd_flash.h34
-rw-r--r--mboxd_lpc.c192
-rw-r--r--mboxd_lpc.h26
-rw-r--r--mboxd_msg.c802
-rw-r--r--mboxd_msg.h44
-rw-r--r--mboxd_windows.c588
-rw-r--r--mboxd_windows.h48
16 files changed, 2959 insertions, 535 deletions
diff --git a/Makefile.am b/Makefile.am
index 2b86e66..0947425 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,6 @@
ACLOCAL_AMFLAGS = -I m4
sbin_PROGRAMS = mboxd
-mboxd_SOURCES = mboxd.c common.c
-mboxd_LDFLAGS = $(SYSTEMD_LIBS)
-mboxd_CFLAGS = $(SYSTEMD_CFLAGS)
+mboxd_SOURCES = mboxd.c common.c mboxd_dbus.c mboxd_flash.c mboxd_lpc.c mboxd_msg.c mboxd_windows.c
+mboxd_LDFLAGS = $(LIBSYSTEMD_LIBS)
+mboxd_CFLAGS = $(LIBSYSTEMD_CFLAGS)
diff --git a/common.h b/common.h
index dc92934..ee537a8 100644
--- a/common.h
+++ b/common.h
@@ -4,16 +4,19 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*
*/
+#ifndef COMMON_H
+#define COMMON_H
+
#ifndef PREFIX
#define PREFIX ""
#endif
@@ -24,6 +27,13 @@ enum {
MBOX_LOG_DEBUG = 2
} verbosity;
+#define MSG_OUT(f_, ...) do { if (verbosity >= MBOX_LOG_DEBUG) { \
+ mbox_log(LOG_INFO, f_, ##__VA_ARGS__); \
+ } } while (0)
+#define MSG_ERR(f_, ...) do { if (verbosity >= MBOX_LOG_VERBOSE) { \
+ mbox_log(LOG_ERR, f_, ##__VA_ARGS__); \
+ } } while (0)
+
void (*mbox_vlog)(int p, const char *fmt, va_list args);
void mbox_log_console(int p, const char *fmt, va_list args);
@@ -39,4 +49,40 @@ uint32_t get_u32(uint8_t *ptr);
void put_u32(uint8_t *ptr, uint32_t val);
+static inline uint32_t align_up(uint32_t val, uint32_t size)
+{
+ return (((val) + (size) - 1) & ~((size) - 1));
+}
+
+static inline uint32_t align_down(uint32_t val, uint32_t size)
+{
+ return ((val) & ~(((size) - 1)));
+}
+
+static inline uint32_t min_u32(uint32_t a, uint32_t b)
+{
+ if (a <= b) {
+ return a;
+ }
+
+ return b;
+}
+
+static inline int log_2(int val)
+{
+ int ret = 0;
+
+ if (val <= 0) {
+ return -1;
+ }
+
+ while (val >>= 1) {
+ ret++;
+ }
+
+ return ret;
+}
+
char *get_dev_mtd(void);
+
+#endif /* COMMON_H */
diff --git a/configure.ac b/configure.ac
index f3376e1..707eefe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,10 @@ AS_IF([test "x$enable_oe_sdk" == "xyes"],
AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags])
)
+PKG_CHECK_MODULES(LIBSYSTEMD, libsystemd, , AC_MSG_ERROR([libsytemd not found]))
+AC_SUBST([LIBSYSTEMD_CFLAGS])
+AC_SUBST([LIBSYSTEMD_LIBS])
+
# Create configured output
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
diff --git a/dbus.h b/dbus.h
new file mode 100644
index 0000000..4e5aa04
--- /dev/null
+++ b/dbus.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016 IBM
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef MBOX_DBUS_H
+#define MBOX_DBUS_H
+
+#define DBUS_NAME "org.openbmc.mboxd"
+#define DOBJ_NAME "/org/openbmc/mboxd"
+
+/* Commands */
+#define DBUS_C_PING 0x00
+#define DBUS_C_DAEMON_STATE 0x01
+#define DBUS_C_RESET 0x02
+#define DBUS_C_SUSPEND 0x03
+#define DBUS_C_RESUME 0x04
+#define DBUS_C_MODIFIED 0x05
+#define DBUS_C_KILL 0x06
+#define DBUS_C_LPC_STATE 0x07
+#define NUM_DBUS_CMDS (DBUS_C_LPC_STATE + 1)
+
+/* Command Args */
+/* Resume */
+#define RESUME_NUM_ARGS 1
+#define RESUME_NOT_MODIFIED 0x00
+#define RESUME_FLASH_MODIFIED 0x01
+
+/* Return Values */
+#define DBUS_SUCCESS 0x00 /* Command Succeded */
+#define E_DBUS_INTERNAL 0x01 /* Internal DBUS Error */
+#define E_DBUS_INVAL 0x02 /* Invalid Command */
+#define E_DBUS_REJECTED 0x03 /* Daemon Rejected Request */
+#define E_DBUS_HARDWARE 0x04 /* BMC Hardware Error */
+
+/* Response Args */
+/* Status */
+#define DAEMON_STATE_NUM_ARGS 1
+#define DAEMON_STATE_ACTIVE 0x00 /* Daemon Active */
+#define DAEMON_STATE_SUSPENDED 0x01 /* Daemon Suspended */
+/* LPC State */
+#define LPC_STATE_NUM_ARGS 1
+#define LPC_STATE_INVALID 0x00 /* Invalid State */
+#define LPC_STATE_FLASH 0x01 /* LPC Maps Flash Directly */
+#define LPC_STATE_MEM 0x02 /* LPC Maps Memory */
+
+struct mbox_dbus_msg {
+ uint8_t cmd;
+ size_t num_args;
+ uint8_t *args;
+};
+
+#endif /* MBOX_DBUS_H */
diff --git a/mbox.h b/mbox.h
index f772558..d9a8d03 100644
--- a/mbox.h
+++ b/mbox.h
@@ -1,53 +1,168 @@
-/* Copyright 2016 IBM
+/*
+ * Copyright 2016 IBM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*
*/
-#define MBOX_C_RESET_STATE 0x01
-#define MBOX_C_GET_MBOX_INFO 0x02
-#define MBOX_C_GET_FLASH_INFO 0x03
-#define MBOX_C_READ_WINDOW 0x04
-#define MBOX_C_CLOSE_WINDOW 0x05
-#define MBOX_C_WRITE_WINDOW 0x06
-#define MBOX_C_WRITE_DIRTY 0x07
-#define MBOX_C_WRITE_FENCE 0x08
-#define MBOX_C_ACK 0x09
-#define MBOX_C_COMPLETED_COMMANDS 0x0a
-
-#define MBOX_R_SUCCESS 0x01
-#define MBOX_R_PARAM_ERROR 0x02
-#define MBOX_R_WRITE_ERROR 0x03
-#define MBOX_R_SYSTEM_ERROR 0x4
-#define MBOX_R_TIMEOUT 0x05
-
-#define MBOX_HOST_PATH "/dev/aspeed-mbox"
-#define MBOX_HOST_TIMEOUT_SEC 1
-#define MBOX_DATA_BYTES 11
-#define MBOX_REG_BYTES 16
-#define MBOX_HOST_BYTE 14
-#define MBOX_BMC_BYTE 15
-
-struct mbox_msg {
- uint8_t command;
- uint8_t seq;
- uint8_t data[MBOX_DATA_BYTES];
- uint8_t response;
+#ifndef MBOX_H
+#define MBOX_H
+
+#include <mtd/mtd-abi.h>
+#include <systemd/sd-bus.h>
+
+enum api_version {
+ API_VERSION_INVAL = 0,
+ API_VERSION_1 = 1,
+ API_VERSION_2 = 2
+};
+
+#define API_MIN_VERSION API_VERSION_1
+#define API_MAX_VERSION API_VERSION_2
+
+#define THIS_NAME "Mailbox Daemon"
+#define SUB_VERSION 0
+
+/* Command Values */
+#define MBOX_C_RESET_STATE 0x01
+#define MBOX_C_GET_MBOX_INFO 0x02
+#define MBOX_C_GET_FLASH_INFO 0x03
+#define MBOX_C_READ_WINDOW 0x04
+#define MBOX_C_CLOSE_WINDOW 0x05
+#define MBOX_C_WRITE_WINDOW 0x06
+#define MBOX_C_WRITE_DIRTY 0x07
+#define MBOX_C_WRITE_FLUSH 0x08
+#define MBOX_C_ACK 0x09
+#define MBOX_C_WRITE_ERASE 0x0a
+#define NUM_MBOX_CMDS MBOX_C_WRITE_ERASE
+
+/* Response Values */
+#define MBOX_R_SUCCESS 0x01
+#define MBOX_R_PARAM_ERROR 0x02
+#define MBOX_R_WRITE_ERROR 0x03
+#define MBOX_R_SYSTEM_ERROR 0x04
+#define MBOX_R_TIMEOUT 0x05
+#define MBOX_R_BUSY 0x06
+#define MBOX_R_WINDOW_ERROR 0x07
+
+/* Argument Flags */
+#define FLAGS_NONE 0x00
+#define FLAGS_SHORT_LIFETIME 0x01
+
+/* BMC Event Notification */
+#define BMC_EVENT_REBOOT 0x01
+#define BMC_EVENT_WINDOW_RESET 0x02
+#define BMC_EVENT_ACK_MASK (BMC_EVENT_REBOOT | \
+ BMC_EVENT_WINDOW_RESET)
+#define BMC_EVENT_FLASH_CTRL_LOST 0x40
+#define BMC_EVENT_DAEMON_READY 0x80
+#define BMC_EVENT_V1_MASK BMC_EVENT_REBOOT
+#define BMC_EVENT_V2_MASK (BMC_EVENT_REBOOT | \
+ BMC_EVENT_WINDOW_RESET | \
+ BMC_EVENT_FLASH_CTRL_LOST | \
+ BMC_EVENT_DAEMON_READY)
+
+/* MBOX Registers */
+#define MBOX_HOST_PATH "/dev/aspeed-mbox"
+#define MBOX_HOST_TIMEOUT_SEC 1
+#define MBOX_ARGS_BYTES 11
+#define MBOX_REG_BYTES 16
+#define MBOX_HOST_EVENT 14
+#define MBOX_BMC_EVENT 15
+
+#define BLOCK_SIZE_SHIFT_V1 12 /* 4K */
+
+/* Window Dirty/Erase bytemap masks */
+#define WINDOW_CLEAN 0x00
+#define WINDOW_DIRTY 0x01
+#define WINDOW_ERASED 0x02
+
+/* Put polled file descriptors first */
+#define DBUS_FD 0
+#define MBOX_FD 1
+#define SIG_FD 2
+#define POLL_FDS 3 /* Number of FDs we poll on */
+#define LPC_CTRL_FD 3
+#define MTD_FD 4
+#define TOTAL_FDS 5
+
+#define MAPS_FLASH (1 << 0)
+#define MAPS_MEM (1 << 1)
+#define STATE_SUSPENDED (1 << 7)
+enum mbox_state {
+ /* Still Initing */
+ UNINITIALISED = 0,
+ /* Active and LPC Maps Flash */
+ ACTIVE_MAPS_FLASH = MAPS_FLASH,
+ /* Suspended and LPC Maps Flash */
+ SUSPEND_MAPS_FLASH = STATE_SUSPENDED | MAPS_FLASH,
+ /* Active and LPC Maps Memory */
+ ACTIVE_MAPS_MEM = MAPS_MEM,
+ /* Suspended and LPC Maps Memory */
+ SUSPEND_MAPS_MEM = STATE_SUSPENDED | MAPS_MEM
+};
+
+#define FLASH_OFFSET_UNINIT 0xFFFFFFFF
+
+struct window_context {
+ void *mem; /* Portion of Reserved Memory Region */
+ uint32_t flash_offset; /* Flash area the window maps (bytes) */
+ uint32_t size; /* Window Size (bytes) power-of-2 */
+ uint8_t *dirty_bmap; /* Bytemap of the dirty/erased state */
+ uint32_t age; /* Used for LRU eviction scheme */
};
-union mbox_regs {
- char raw[MBOX_REG_BYTES];
- struct mbox_msg msg;
+struct window_list {
+ uint32_t num;
+ uint32_t max_age;
+ uint32_t default_size;
+ struct window_context *window;
};
+struct mbox_context {
+/* System State */
+ enum mbox_state state;
+ enum api_version version;
+ struct pollfd fds[TOTAL_FDS];
+ sd_bus *bus;
+ bool terminate;
+ uint8_t bmc_events;
+
+/* Window State */
+ /* The window list struct containing all current "windows" */
+ struct window_list windows;
+ /* The window the host is currently pointed at */
+ struct window_context *current;
+ /* Is the current window a write one */
+ bool current_is_write;
+
+/* Memory & Flash State */
+ /* Reserved Memory Region */
+ void *mem;
+ /* Reserved Mem Size (bytes) */
+ uint32_t mem_size;
+ /* LPC Bus Base Address (bytes) */
+ uint32_t lpc_base;
+ /* Flash size from command line (bytes) */
+ uint32_t flash_size;
+ /* Bytemap of the erased state of the entire flash */
+ uint8_t *flash_bmap;
+ /* Erase size (as a shift) */
+ uint32_t erase_size_shift;
+ /* Block size (as a shift) */
+ uint32_t block_size_shift;
+ /* Actual Flash Info */
+ struct mtd_info_user mtd_info;
+};
+#endif /* MBOX_H */
diff --git a/mboxd.c b/mboxd.c
index 8503b49..6f060d8 100644
--- a/mboxd.c
+++ b/mboxd.c
@@ -1,19 +1,23 @@
-/* Copyright 2016 IBM
+/*
+ * Mailbox Daemon Implementation
+ *
+ * Copyright 2016 IBM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*
*/
+#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
@@ -32,577 +36,359 @@
#include <sys/stat.h>
#include <sys/timerfd.h>
#include <sys/types.h>
+#include <sys/signalfd.h>
#include <time.h>
#include <unistd.h>
#include <inttypes.h>
-
-#include <mtd/mtd-abi.h>
-
-#include <linux/aspeed-lpc-ctrl.h>
+#include <systemd/sd-bus.h>
#include "mbox.h"
#include "common.h"
+#include "dbus.h"
+#include "mboxd_dbus.h"
+#include "mboxd_flash.h"
+#include "mboxd_lpc.h"
+#include "mboxd_msg.h"
+#include "mboxd_windows.h"
+
+#define USAGE \
+"\nUsage: %s [-V | --version] [-h | --help] [-v[v] | --verbose] [-s | --syslog]\n" \
+"\t\t-n | --window-num <num>\n" \
+"\t\t-w | --window-size <size>M\n" \
+"\t\t-f | --flash <size>[K|M]\n\n" \
+"\t-v | --verbose\t\tBe [more] verbose\n" \
+"\t-s | --syslog\t\tLog output to syslog (pointless without -v)\n" \
+"\t-n | --window-num\tThe number of windows\n" \
+"\t-w | --window-size\tThe window size (power of 2) in MB\n" \
+"\t-f | --flash\t\tSize of flash in [K|M] bytes\n\n"
+
+static int poll_loop(struct mbox_context *context)
+{
+ int rc = 0, i;
-#define LPC_CTRL_PATH "/dev/aspeed-lpc-ctrl"
-
-
-/* Put pulled fds first */
-#define MBOX_FD 0
-#define POLL_FDS 1
-#define LPC_CTRL_FD 1
-#define MTD_FD 2
-#define TOTAL_FDS 3
+ /* Set POLLIN on polling file descriptors */
+ for (i = 0; i < POLL_FDS; i++) {
+ context->fds[i].events = POLLIN;
+ }
-#define ALIGN_UP(_v, _a) (((_v) + (_a) - 1) & ~((_a) - 1))
+ while (1) {
+ rc = poll(context->fds, POLL_FDS, -1);
-#define MSG_OUT(f_, ...) do { if (verbosity != MBOX_LOG_NONE) { mbox_log(LOG_INFO, f_, ##__VA_ARGS__); } } while(0)
-#define MSG_ERR(f_, ...) do { if (verbosity != MBOX_LOG_NONE) { mbox_log(LOG_ERR, f_, ##__VA_ARGS__); } } while(0)
+ if (rc < 0) { /* Error */
+ MSG_ERR("Error from poll(): %s\n", strerror(errno));
+ break; /* This should mean we clean up nicely */
+ }
-#define BOOT_HICR7 0x30000e00U
-#define BOOT_HICR8 0xfe0001ffU
+ /* Event on Polled File Descriptor - Handle It */
+ if (context->fds[SIG_FD].revents & POLLIN) { /* Signal */
+ struct signalfd_siginfo info = { 0 };
-struct mbox_context {
- struct pollfd fds[TOTAL_FDS];
- void *lpc_mem;
- uint32_t base;
- uint32_t size;
- uint32_t pgsize;
- bool dirty;
- uint32_t dirtybase;
- uint32_t dirtysize;
- struct mtd_info_user mtd_info;
- uint32_t flash_size;
-};
+ rc = read(context->fds[SIG_FD].fd, (void *) &info,
+ sizeof(info));
+ if (rc != sizeof(info)) {
+ MSG_ERR("Error reading signal event: %s\n",
+ strerror(errno));
+ }
-static int running = 1;
-static int sighup = 0;
+ switch (info.ssi_signo) {
+ case SIGINT:
+ case SIGTERM:
+ MSG_OUT("Caught Signal - Exiting...\n");
+ context->terminate = true;
+ break;
+ case SIGHUP:
+ /* Host didn't request reset -> Notify it */
+ reset_all_windows(context, SET_BMC_EVENT);
+ rc = point_to_flash(context);
+ if (rc < 0) {
+ MSG_ERR("WARNING: Failed to point the "
+ "LPC bus back to flash on "
+ "SIGHUP\nIf the host requires "
+ "this expect problems...\n");
+ }
+ break;
+ default:
+ MSG_ERR("Unhandled Signal: %d\n",
+ info.ssi_signo);
+ break;
+ }
+ }
+ if (context->fds[DBUS_FD].revents & POLLIN) { /* DBUS */
+ while ((rc = sd_bus_process(context->bus, NULL)) > 0);
+ if (rc < 0) {
+ MSG_ERR("Error handling DBUS event: %s\n",
+ strerror(-rc));
+ }
+ }
+ if (context->terminate) {
+ break; /* This should mean we clean up nicely */
+ }
+ if (context->fds[MBOX_FD].revents & POLLIN) { /* MBOX */
+ rc = dispatch_mbox(context);
+ if (rc < 0) {
+ MSG_ERR("Error handling MBOX event\n");
+ }
+ }
+ }
-static int point_to_flash(struct mbox_context *context)
-{
- struct aspeed_lpc_ctrl_mapping map = { 0 };
- int r = 0;
-
- /*
- * Point it to the real flash for sanity.
- *
- * This function assumes 32MB of flash which means that that
- * hostboot expects flash to be at 0x0e000000 - 0x0fffffff on the
- * LPC bus. If the machine actually has 64MB of flash then the
- * map.addr should be 0x0c000000. TODO
- *
- * Until hostboot learns how to talk to this daemon this hardcode will
- * get hostboot going. Furthermore, when hostboot does learn to talk
- * then this mapping is unnecessary and this code should be removed.
- */
-
- /*
- * The mask is because the top nibble is the host LPC FW space, we
- * want space 0. The struct has been zeroed, best to be explicit
- * though.
- */
- map.addr = (0UL - context->flash_size) & 0x0fffffff;
- map.size = context->flash_size;
- map.offset = 0;
- map.window_type = ASPEED_LPC_CTRL_WINDOW_FLASH;
- map.window_id = 0; /* Theres only one */
-
- MSG_OUT("Pointing HOST LPC bus at the actual flash\n");
- MSG_OUT("Assuming %dMB of flash: HOST LPC 0x%08x\n", context->flash_size >> 20,
- map.addr);
-
- if (ioctl(context->fds[LPC_CTRL_FD].fd, ASPEED_LPC_CTRL_IOCTL_MAP, &map) == -1) {
- r = -errno;
- MSG_ERR("Couldn't MAP the host LPC bus to the platform flash\n");
+ /* Best to reset windows and point back to flash for safety */
+ /* Host didn't request reset -> Notify it */
+ reset_all_windows(context, SET_BMC_EVENT);
+ rc = point_to_flash(context);
+ /* Not much we can do if this fails */
+ if (rc < 0) {
+ MSG_ERR("WARNING: Failed to point the LPC bus back to flash\n"
+ "If the host requires this expect problems...\n");
}
- return r;
+ return rc;
}
-static int flash_write(struct mbox_context *context, uint32_t pos, uint32_t len)
+static int init_signals(struct mbox_context *context, sigset_t *set)
{
int rc;
- struct erase_info_user erase_info = {
- .start = pos,
- };
-
- assert(context);
-
- erase_info.length = ALIGN_UP(len, context->mtd_info.erasesize);
- MSG_OUT("Erasing 0x%08x for 0x%08x (aligned: 0x%08x)\n", pos, len, erase_info.length);
- if (ioctl(context->fds[MTD_FD].fd, MEMERASE, &erase_info) == -1) {
- MSG_ERR("Couldn't MEMERASE ioctl, flash write lost: %s\n", strerror(errno));
- return -1;
+ /* Block SIGHUPs, SIGTERMs and SIGINTs */
+ sigemptyset(set);
+ sigaddset(set, SIGHUP);
+ sigaddset(set, SIGINT);
+ sigaddset(set, SIGTERM);
+ rc = sigprocmask(SIG_BLOCK, set, NULL);
+ if (rc < 0) {
+ MSG_ERR("Failed to set SIG_BLOCK mask %s\n", strerror(errno));
+ return rc;
}
- if (lseek(context->fds[MTD_FD].fd, pos, SEEK_SET) == (off_t) -1) {
- MSG_ERR("Couldn't seek to 0x%08x into MTD, flash write lost: %s\n", pos, strerror(errno));
- return -1;
- }
-
- while (erase_info.length) {
- rc = write(context->fds[MTD_FD].fd, context->lpc_mem + pos, erase_info.length);
- if (rc == -1) {
- MSG_ERR("Couldn't write to flash! Flash write lost: %s\n", strerror(errno));
- return -1;
- }
- erase_info.length -= rc;
- pos += rc;
+ /* Get Signal File Descriptor */
+ rc = signalfd(-1, set, SFD_NONBLOCK);
+ if (rc < 0) {
+ MSG_ERR("Failed to get signalfd %s\n", strerror(errno));
+ return rc;
}
+ context->fds[SIG_FD].fd = rc;
return 0;
}
-/* TODO: Add come consistency around the daemon exiting and either
- * way, ensuring it responds.
- * I'm in favour of an approach where it does its best to stay alive
- * and keep talking, the hacky prototype was written the other way.
- * This function is now inconsistent
- */
-static int dispatch_mbox(struct mbox_context *context)
+static void usage(const char *name)
{
- int r = 0;
- int len;
- off_t pos;
- uint8_t byte;
- union mbox_regs resp, req = { 0 };
- uint16_t sizepg, basepg, dirtypg;
- uint32_t dirtycount;
- struct aspeed_lpc_ctrl_mapping map = { 0 };
-
- assert(context);
-
- map.addr = context->base;
- map.size = context->size;
- map.offset = 0;
- map.window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY;
- map.window_id = 0; /* Theres only one */
-
- MSG_OUT("Dispatched to mbox\n");
- r = read(context->fds[MBOX_FD].fd, &req, sizeof(req.raw));
- if (r < 0) {
- r = -errno;
- MSG_ERR("Couldn't read: %s\n", strerror(errno));
- goto out;
- }
- if (r < sizeof(req.msg)) {
- MSG_ERR("Short read: %d expecting %zu\n", r, sizeof(req.msg));
- r = -1;
- goto out;
- }
+ printf(USAGE, name);
+}
- /* We are NOT going to update the last two 'status' bytes */
- memcpy(&resp, &req, sizeof(req.msg));
-
- sizepg = context->size >> context->pgsize;
- basepg = context->base >> context->pgsize;
- MSG_OUT("Got data in with command %d\n", req.msg.command);
- switch (req.msg.command) {
- case MBOX_C_RESET_STATE:
- /* Called by early hostboot? TODO */
- resp.msg.response = MBOX_R_SUCCESS;
- r = point_to_flash(context);
- if (r) {
- resp.msg.response = MBOX_R_SYSTEM_ERROR;
- MSG_ERR("Couldn't point the LPC BUS back to actual flash\n");
- }
- break;
- case MBOX_C_GET_MBOX_INFO:
- /* TODO Freak if data.data[0] isn't 1 */
- resp.msg.data[0] = 1;
- put_u16(&resp.msg.data[1], sizepg);
- put_u16(&resp.msg.data[3], sizepg);
- resp.msg.response = MBOX_R_SUCCESS;
- /* Wow that can't stay negated thats horrible */
- MSG_OUT("LPC_CTRL_IOCTL_MAP to 0x%08x for 0x%08x\n", map.addr, map.size);
- r = ioctl(context->fds[LPC_CTRL_FD].fd,
- ASPEED_LPC_CTRL_IOCTL_MAP, &map);
- if (r < 0) {
- r = -errno;
- resp.msg.response = MBOX_R_SYSTEM_ERROR;
- MSG_ERR("Couldn't MAP ioctl(): %s\n", strerror(errno));
- }
- break;
- case MBOX_C_GET_FLASH_INFO:
- put_u32(&resp.msg.data[0], context->flash_size);
- put_u32(&resp.msg.data[4], context->mtd_info.erasesize);
- resp.msg.response = MBOX_R_SUCCESS;
+static bool parse_cmdline(int argc, char **argv,
+ struct mbox_context *context)
+{
+ char *endptr;
+ int opt, i;
+
+ static const struct option long_options[] = {
+ { "flash", required_argument, 0, 'f' },
+ { "window-size", optional_argument, 0, 'w' },
+ { "window-num", optional_argument, 0, 'n' },
+ { "verbose", no_argument, 0, 'v' },
+ { "syslog", no_argument, 0, 's' },
+ { "version", no_argument, 0, 'V' },
+ { "help", no_argument, 0, 'h' },
+ { 0, 0, 0, 0 }
+ };
+
+ verbosity = MBOX_LOG_NONE;
+ mbox_vlog = &mbox_log_console;
+
+ /* Default to 1 window of size flash_size */
+ context->windows.default_size = context->flash_size;
+ context->windows.num = 1;
+ context->current = NULL; /* No current window */
+
+ while ((opt = getopt_long(argc, argv, "f:w::n::vsVh", long_options, NULL))
+ != -1) {
+ switch (opt) {
+ case 0:
break;
- case MBOX_C_READ_WINDOW:
- /*
- * We could probably play tricks with LPC mapping.
- * That would require kernel involvement.
- * We could also always copy the relevant flash part to
- * context->base even if it turns out that offset is in
- * the window...
- * This approach is easiest.
- */
- if (context->dirty) {
- r = read(context->fds[MTD_FD].fd, context->lpc_mem, context->size);
- if (r != context->size) {
- MSG_ERR("Short read: %d expecting %"PRIu32"\n", r, context->size);
- goto out;
- }
+ case 'f':
+ context->flash_size = strtol(optarg, &endptr, 10);
+ if (optarg == endptr) {
+ fprintf(stderr, "Unparseable flash size\n");
+ return false;
}
- basepg += get_u16(&req.msg.data[0]);
- put_u16(&resp.msg.data[0], basepg);
- resp.msg.response = MBOX_R_SUCCESS;
- context->dirty = false;
- break;
- case MBOX_C_CLOSE_WINDOW:
- context->dirty = true;
- break;
- case MBOX_C_WRITE_WINDOW:
- basepg += get_u16(&req.msg.data[0]);
- put_u16(&resp.msg.data[0], basepg);
- resp.msg.response = MBOX_R_SUCCESS;
- context->dirtybase = basepg << context->pgsize;
- break;
- /* Optimise these later */
- case MBOX_C_WRITE_DIRTY:
- case MBOX_C_WRITE_FENCE:
- dirtypg = get_u16(&req.msg.data[0]);
- dirtycount = get_u32(&req.msg.data[2]);
- if (dirtycount == 0) {
- resp.msg.response = MBOX_R_PARAM_ERROR;
+ switch (*endptr) {
+ case '\0':
break;
- }
- /*
- * dirtypg is actually offset within window so we probs
- * need to know if the window isn't at zero
- */
- if (flash_write(context, dirtypg << context->pgsize, dirtycount) != 0) {
- resp.msg.response = MBOX_R_WRITE_ERROR;
+ case 'M':
+ context->flash_size <<= 10;
+ case 'K':
+ context->flash_size <<= 10;
break;
+ default:
+ fprintf(stderr, "Unknown units '%c'\n",
+ *endptr);
+ return false;
}
- resp.msg.response = MBOX_R_SUCCESS;
break;
- case MBOX_C_ACK:
- resp.msg.response = MBOX_R_SUCCESS;
- pos = lseek(context->fds[MBOX_FD].fd, MBOX_BMC_BYTE, SEEK_SET);
- if (pos != MBOX_BMC_BYTE) {
- r = -errno;
- MSG_ERR("Couldn't lseek() to byte %d: %s\n", MBOX_BMC_BYTE,
- strerror(errno));
- }
- /*
- * NAND what is in the hardware and the request.
- * This prevents the host being able to SET bits, it can
- * only request set ones be cleared.
- */
- byte = ~(req.msg.data[0] & req.raw[MBOX_BMC_BYTE]);
- len = write(context->fds[MBOX_FD].fd, &byte, 1);
- if (len != 1) {
- r = -errno;
- MSG_ERR("Couldn't write to BMC status reg: %s\n",
- strerror(errno));
+ case 'n':
+ context->windows.num = strtol(argv[optind], &endptr,
+ 10);
+ if (optarg == endptr || *endptr != '\0') {
+ fprintf(stderr, "Unparseable window num\n");
+ return false;
}
- pos = lseek(context->fds[MBOX_FD].fd, 0, SEEK_SET);
- if (pos != 0) {
- r = -errno;
- MSG_ERR("Couldn't reset MBOX offset to zero\n");
+ break;
+ case 'w':
+ context->windows.default_size = strtol(argv[optind],
+ &endptr, 10);
+ context->windows.default_size <<= 20; /* Given in MB */
+ if (optarg == endptr || (*endptr != '\0' &&
+ *endptr != 'M')) {
+ fprintf(stderr, "Unparseable window size\n");
+ return false;
}
break;
- case MBOX_C_COMPLETED_COMMANDS:
- /* This implementation always completes before responding */
- resp.msg.data[0] = 0;
- resp.msg.response = MBOX_R_SUCCESS;
+ case 'v':
+ verbosity++;
break;
+ case 's':
+ /* Avoid a double openlog() */
+ if (mbox_vlog != &vsyslog) {
+ openlog(PREFIX, LOG_ODELAY, LOG_DAEMON);
+ mbox_vlog = &vsyslog;
+ }
+ break;
+ case 'V':
+ printf("%s v%d.%.2d\n", THIS_NAME, API_MAX_VERSION,
+ SUB_VERSION);
+ exit(0);
+ case 'h':
+ return false; /* This will print the usage message */
default:
- MSG_ERR("UNKNOWN MBOX COMMAND\n");
- resp.msg.response = MBOX_R_PARAM_ERROR;
- r = -1;
+ return false;
+ }
}
- MSG_OUT("Writing response to MBOX regs\n");
- len = write(context->fds[MBOX_FD].fd, &resp, sizeof(resp.msg));
- if (len < sizeof(resp.msg)) {
- r = -errno;
- MSG_ERR("Didn't write the full response\n");
+ if (!context->flash_size) {
+ fprintf(stderr, "Must specify a non-zero flash size\n");
+ return false;
}
-out:
- return r;
-}
+ if (!context->windows.num) {
+ fprintf(stderr, "Must specify a non-zero number of windows\n"
+ "If unsure - select 4 (-n 4)\n");
+ return false;
+ }
+ if (!context->windows.default_size) {
+ fprintf(stderr, "Must specify a non-zero window size\n"
+ "If unsure - select 1M (-w 1)\n");
+ return false;
+ }
-#define CHUNKSIZE (64 * 1024)
+ MSG_OUT("Flash size: 0x%.8x\n", context->flash_size);
+ MSG_OUT("Number of Windows: %d\n", context->windows.num);
+ MSG_OUT("Window size: 0x%.8x\n", context->windows.default_size);
-int copy_flash(struct mbox_context *context)
-{
- int r;
- int readsize = CHUNKSIZE;
- int offset = 0;
-
- /*
- * Copy flash into RAM early, same time.
- * The kernel has created the LPC->AHB mapping also, which means
- * flash should work.
- * Ideally we tell the kernel whats up and when to do stuff...
- */
- MSG_OUT("Loading flash into ram at %p for 0x%08x bytes\n",
- context->lpc_mem, context->size);
- if (lseek(context->fds[MTD_FD].fd, 0, SEEK_SET) != 0) {
- r = -errno;
- MSG_ERR("Couldn't reset MBOX pos to zero\n");
- return r;
+ context->windows.window = calloc(context->windows.num,
+ sizeof(*context->windows.window));
+ if (!context->windows.window) {
+ MSG_ERR("Memory allocation failed\n");
+ free(context);
+ exit(1);
}
- while (readsize) {
- r = read(context->fds[MTD_FD].fd, context->lpc_mem + offset, readsize);
- if (r != readsize) {
- r = -errno;
- MSG_ERR("Couldn't copy mtd into ram: %d\n", r);
- return r;
- }
- offset += readsize;
- readsize = context->size - offset;
- if (readsize > CHUNKSIZE)
- readsize = CHUNKSIZE;
+
+ for (i = 0; i < context->windows.num; i++) {
+ init_window_state(&context->windows.window[i],
+ context->windows.default_size);
}
- return 0;
-}
-void signal_hup(int signum, siginfo_t *info, void *uc)
-{
- sighup = 1;
-}
+ if (verbosity) {
+ MSG_OUT("%s logging\n", verbosity == MBOX_LOG_DEBUG ? "Debug" :
+ "Verbose");
+ }
-static void usage(const char *name)
-{
- fprintf(stderr, "Usage %s [ -v[v] | --syslog ] --flash=size[K | M]\n", name);
- fprintf(stderr, "\t--flash size[K | M]\t Map the flash for the according to 'size' in Kilobytes or Megabytes\n");
- fprintf(stderr, "\t--verbose\t Be [more] verbose\n");
- fprintf(stderr, "\t--syslog\t Log output to syslog (pointless without -v)\n\n");
+ return true;
}
-int main(int argc, char *argv[])
+int main(int argc, char **argv)
{
struct mbox_context *context;
- const char *name = argv[0];
- char *pnor_filename = NULL;
- int opt, polled, r, i;
- struct aspeed_lpc_ctrl_mapping map = { 0 };
- struct sigaction act;
- char *endptr;
-
- static const struct option long_options[] = {
- { "flash", required_argument, 0, 'f' },
- { "verbose", no_argument, 0, 'v' },
- { "syslog", no_argument, 0, 's' },
- { 0, 0, 0, 0 }
- };
+ char *name = argv[0];
+ sigset_t set;
+ int rc, i;
context = calloc(1, sizeof(*context));
- for (i = 0; i < TOTAL_FDS; i++)
- context->fds[i].fd = -1;
-
- mbox_vlog = &mbox_log_console;
- while ((opt = getopt_long(argc, argv, "fv", long_options, NULL)) != -1) {
- switch (opt) {
- case 0:
- break;
- case 'f':
- context->flash_size = strtol(optarg, &endptr, 0);
- if (optarg == endptr) {
- fprintf(stderr, "Unparseable flash size\n");
- usage(name);
- exit(EXIT_FAILURE);
- }
- if (*endptr == 'K') {
- context->flash_size <<= 10;
- } else if (*endptr == 'M') {
- context->flash_size <<= 20;
- } else if (*endptr != '\0') { /* Unknown units */
- fprintf(stderr, "Unknown units '%c'\n", *endptr);
- usage(name);
- exit(EXIT_FAILURE);
- }
- break;
- case 'v':
- verbosity++;
- break;
- case 's':
- /* Avoid a double openlog() */
- if (mbox_vlog != &vsyslog) {
- openlog(PREFIX, LOG_ODELAY, LOG_DAEMON);
- mbox_vlog = &vsyslog;
- }
- break;
- default:
- usage(name);
- exit(EXIT_FAILURE);
- }
+ if (!context) {
+ fprintf(stderr, "Memory allocation failed\n");
+ exit(1);
}
- if (context->flash_size == 0) {
- fprintf(stderr, "Must specify a non-zero flash size\n");
+ if (!parse_cmdline(argc, argv, context)) {
usage(name);
- exit(EXIT_FAILURE);
+ free(context);
+ exit(0);
}
- if (verbosity == MBOX_LOG_VERBOSE)
- MSG_OUT("Verbose logging\n");
-
- if (verbosity == MBOX_LOG_DEBUG)
- MSG_OUT("Debug logging\n");
-
- MSG_OUT("Registering SigHUP hander\n");
- act.sa_sigaction = signal_hup;
- sigemptyset(&act.sa_mask);
- act.sa_flags = SA_SIGINFO;
- if (sigaction(SIGHUP, &act, NULL) < 0) {
- perror("Registering SIGHUP");
- exit(1);
+ for (i = 0; i < TOTAL_FDS; i++) {
+ context->fds[i].fd = -1;
}
- sighup = 0;
- MSG_OUT("Starting\n");
+ MSG_OUT("Starting Daemon\n");
- MSG_OUT("Opening %s\n", MBOX_HOST_PATH);
- context->fds[MBOX_FD].fd = open(MBOX_HOST_PATH, O_RDWR | O_NONBLOCK);
- if (context->fds[MBOX_FD].fd < 0) {
- r = -errno;
- MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n",
- MBOX_HOST_PATH, strerror(errno));
+ rc = init_signals(context, &set);
+ if (rc) {
goto finish;
}
- MSG_OUT("Opening %s\n", LPC_CTRL_PATH);
- context->fds[LPC_CTRL_FD].fd = open(LPC_CTRL_PATH, O_RDWR | O_SYNC);
- if (context->fds[LPC_CTRL_FD].fd < 0) {
- r = -errno;
- MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n",
- LPC_CTRL_PATH, strerror(errno));
+ rc = init_mbox_dev(context);
+ if (rc) {
goto finish;
}
- MSG_OUT("Getting buffer size...\n");
- /* This may become more variable in the future */
- context->pgsize = 12; /* 4K */
- map.window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY;
- map.window_id = 0; /* Theres only one */
- if (ioctl(context->fds[LPC_CTRL_FD].fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE,
- &map) < 0) {
- r = -errno;
- MSG_OUT("fail\n");
- MSG_ERR("Couldn't get lpc control buffer size: %s\n", strerror(-r));
- goto finish;
- }
- /* And strip the first nibble, LPC access speciality */
- context->size = map.size;
- context->base = -context->size & 0x0FFFFFFF;
-
- /* READ THE COMMENT AT THE START OF THIS FUNCTION! */
- r = point_to_flash(context);
- if (r) {
- MSG_ERR("Failed to point the LPC BUS at the actual flash: %s\n",
- strerror(-r));
+ rc = init_lpc_dev(context);
+ if (rc) {
goto finish;
}
- MSG_OUT("Mapping %s for %u\n", LPC_CTRL_PATH, context->size);
- context->lpc_mem = mmap(NULL, context->size, PROT_READ | PROT_WRITE, MAP_SHARED,
- context->fds[LPC_CTRL_FD].fd, 0);
- if (context->lpc_mem == MAP_FAILED) {
- r = -errno;
- MSG_ERR("Didn't manage to mmap %s: %s\n", LPC_CTRL_PATH, strerror(errno));
+ /* We've found the reserved memory region -> we can assign to windows */
+ rc = init_window_mem(context);
+ if (rc) {
goto finish;
}
- pnor_filename = get_dev_mtd();
- if (!pnor_filename) {
- MSG_ERR("Couldn't find the PNOR /dev/mtd partition\n");
- r = -1;
+ rc = init_flash_dev(context);
+ if (rc) {
goto finish;
}
- MSG_OUT("Opening %s\n", pnor_filename);
- context->fds[MTD_FD].fd = open(pnor_filename, O_RDWR);
- if (context->fds[MTD_FD].fd < 0) {
- r = -errno;
- MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n",
- pnor_filename, strerror(errno));
+ rc = init_mboxd_dbus(context);
+ if (rc) {
goto finish;
}
- if (ioctl(context->fds[MTD_FD].fd, MEMGETINFO, &context->mtd_info) == -1) {
- MSG_ERR("Couldn't get information about MTD: %s\n", strerror(errno));
- return -1;
- }
-
- if (copy_flash(context))
+ /* Set the LPC bus mapping to point to the physical flash device */
+ rc = point_to_flash(context);
+ if (rc) {
goto finish;
-
- context->fds[MBOX_FD].events = POLLIN;
-
- /* Test the single write facility by setting all the regs to 0xFF */
- MSG_OUT("Setting all MBOX regs to 0xff individually...\n");
- for (i = 0; i < MBOX_REG_BYTES; i++) {
- uint8_t byte = 0xff;
- off_t pos;
- int len;
-
- pos = lseek(context->fds[MBOX_FD].fd, i, SEEK_SET);
- if (pos != i) {
- MSG_ERR("Couldn't lseek() to byte %d: %s\n", i,
- strerror(errno));
- break;
- }
- len = write(context->fds[MBOX_FD].fd, &byte, 1);
- if (len != 1) {
- MSG_ERR("Couldn't write MBOX reg %d: %s\n", i,
- strerror(errno));
- break;
- }
}
- if (lseek(context->fds[MBOX_FD].fd, 0, SEEK_SET) != 0) {
- r = -errno;
- MSG_ERR("Couldn't reset MBOX pos to zero\n");
+
+ rc = set_bmc_events(context, BMC_EVENT_DAEMON_READY, SET_BMC_EVENT);
+ if (rc) {
goto finish;
}
- MSG_OUT("Entering polling loop\n");
- while (running) {
- polled = poll(context->fds, POLL_FDS, 1000);
- if (polled == 0)
- continue;
- if ((polled == -1) && (errno != -EINTR) && (sighup == 1)) {
- /* Got sighup. reset to point to flash and
- * reread flash */
- r = point_to_flash(context);
- if (r) {
- goto finish;
- }
- r = copy_flash(context);
- if (r)
- goto finish;
- sighup = 0;
- continue;
- }
- if (polled < 0) {
- r = -errno;
- MSG_ERR("Error from poll(): %s\n", strerror(errno));
- break;
- }
- r = dispatch_mbox(context);
- if (r < 0) {
- MSG_ERR("Error handling MBOX event: %s\n", strerror(-r));
- break;
- }
- }
+ MSG_OUT("Entering Polling Loop\n");
+ rc = poll_loop(context);
- MSG_OUT("Exiting\n");
+ MSG_OUT("Exiting Poll Loop: %d\n", rc);
finish:
- if (context->lpc_mem)
- munmap(context->lpc_mem, context->size);
-
- free(pnor_filename);
- close(context->fds[MTD_FD].fd);
- close(context->fds[LPC_CTRL_FD].fd);
- close(context->fds[MBOX_FD].fd);
+ MSG_OUT("Daemon Exiting...\n");
+ clr_bmc_events(context, BMC_EVENT_DAEMON_READY, SET_BMC_EVENT);
+
+ free_mboxd_dbus(context);
+ free_flash_dev(context);
+ free_lpc_dev(context);
+ free_mbox_dev(context);
+ free_window_dirty_bytemap(context);
+ free(context->windows.window);
free(context);
- return r;
+ return rc;
}
-
diff --git a/mboxd_dbus.c b/mboxd_dbus.c
new file mode 100644
index 0000000..3b09bb1
--- /dev/null
+++ b/mboxd_dbus.c
@@ -0,0 +1,372 @@
+/*
+ * Mailbox Daemon DBUS Helpers
+ *
+ * Copyright 2016 IBM
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/timerfd.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <systemd/sd-bus.h>
+
+#include "mbox.h"
+#include "common.h"
+#include "dbus.h"
+#include "mboxd_dbus.h"
+#include "mboxd_windows.h"
+#include "mboxd_msg.h"
+#include "mboxd_lpc.h"
+#include "mboxd_flash.h"
+
+typedef int (*mboxd_dbus_handler)(struct mbox_context *, struct mbox_dbus_msg *,
+ struct mbox_dbus_msg *);
+
+/* DBUS Functions */
+
+/*
+ * Command: DBUS Ping
+ * Ping the daemon
+ *
+ * Args: NONE
+ * Resp: NONE
+ */
+static int dbus_handle_ping(struct mbox_context *context,
+ struct mbox_dbus_msg *req,
+ struct mbox_dbus_msg *resp)
+{
+ return 0;
+}
+
+/*
+ * Command: DBUS Status
+ * Get the status of the daemon
+ *
+ * Args: NONE
+ * Resp[0]: Status Code
+ */
+static int dbus_handle_daemon_state(struct mbox_context *context,
+ struct mbox_dbus_msg *req,
+ struct mbox_dbus_msg *resp)
+{
+ resp->num_args = DAEMON_STATE_NUM_ARGS;
+ resp->args = calloc(resp->num_args, sizeof(*resp->args));
+ resp->args[0] = (context->state & STATE_SUSPENDED) ?
+ DAEMON_STATE_SUSPENDED : DAEMON_STATE_ACTIVE;
+
+ return 0;
+}
+
+/*
+ * Command: DBUS LPC State
+ * Get the state of the lpc bus mapping (whether it points to memory or flash
+ *
+ * Args: NONE
+ * Resp[0]: LPC Bus State Code
+ */
+static int dbus_handle_lpc_state(struct mbox_context *context,
+ struct mbox_dbus_msg *req,
+ struct mbox_dbus_msg *resp)
+{
+ resp->num_args = LPC_STATE_NUM_ARGS;
+ resp->args = calloc(resp->num_args, sizeof(*resp->args));
+ if ((context->state & MAPS_MEM) && !(context->state & MAPS_FLASH)) {
+ resp->args[0] = LPC_STATE_MEM;
+ } else if (!(context->state & MAPS_MEM) &&
+ (context->state & MAPS_FLASH)) {
+ resp->args[0] = LPC_STATE_FLASH;
+ } else {
+ resp->args[0] = LPC_STATE_INVALID;
+ }
+
+ return 0;
+}
+
+/*
+ * Command: DBUS Reset
+ * Reset the daemon state, final operation TBA.
+ * For now we just point the lpc mapping back at the flash.
+ *
+ * Args: NONE
+ * Resp: NONE
+ */
+static int dbus_handle_reset(struct mbox_context *context,
+ struct mbox_dbus_msg *req,
+ struct mbox_dbus_msg *resp)
+{
+ int rc;
+
+ /* We don't let the host access flash if the daemon is suspened */
+ if (context->state & STATE_SUSPENDED) {
+ return -E_DBUS_REJECTED;
+ }
+
+ /*
+ * This will close (and flush) the current window and point the lpc bus
+ * mapping back to flash. Better set the bmc event to notify the host
+ * of this.
+ */
+ reset_all_windows(context, SET_BMC_EVENT);
+ rc = point_to_flash(context);
+ if (rc < 0) {
+ return -E_DBUS_HARDWARE;
+ }
+
+ return 0;
+}
+
+/*
+ * Command: DBUS Kill
+ * Stop the daemon
+ *
+ * Args: NONE
+ * Resp: NONE
+ */
+static int dbus_handle_kill(struct mbox_context *context,
+ struct mbox_dbus_msg *req,
+ struct mbox_dbus_msg *resp)
+{
+ context->terminate = 1;
+
+ MSG_OUT("DBUS Kill - Exiting...\n");
+
+ return 0;
+}
+
+/*
+ * Command: DBUS Flash Modified
+ * Used to notify the daemon that the flash has been modified out from under
+ * it - We need to reset all out windows to ensure flash will be reloaded
+ * when a new window is opened.
+ * Note: We don't flush any previously opened windows
+ *
+ * Args: NONE
+ * Resp: NONE
+ */
+static int dbus_handle_modified(struct mbox_context *context,
+ struct mbox_dbus_msg *req,
+ struct mbox_dbus_msg *resp)
+{
+ /* Flash has been modified - can no longer trust our erased bytemap */
+ set_flash_bytemap(context, 0, context->flash_size, FLASH_DIRTY);
+
+ /* Force daemon to reload all windows -> Set BMC event to notify host */
+ reset_all_windows(context, SET_BMC_EVENT);
+
+ return 0;
+}
+
+/*
+ * Command: DBUS Suspend
+ * Suspend the daemon to inhibit it from performing flash accesses.
+ * This is used to synchronise access to the flash between the daemon and
+ * directly from the BMC.
+ *
+ * Args: NONE
+ * Resp: NONE
+ */
+static int dbus_handle_suspend(struct mbox_context *context,
+ struct mbox_dbus_msg *req,
+ struct mbox_dbus_msg *resp)
+{
+ int rc;
+
+ if (context->state & STATE_SUSPENDED) {
+ /* Already Suspended */
+ return DBUS_SUCCESS;
+ }
+
+ /* Nothing to check - Just set the bit to notify the host */
+ rc = set_bmc_events(context, BMC_EVENT_FLASH_CTRL_LOST, SET_BMC_EVENT);
+ if (rc < 0) {
+ return -E_DBUS_HARDWARE;
+ }
+
+ context->state |= STATE_SUSPENDED;
+
+ return rc;
+}
+
+/*
+ * Command: DBUS Resume
+ * Resume the daemon to let it perform flash accesses again.
+ *
+ * Args[0]: Flash Modified (0 - no | 1 - yes)
+ * Resp: NONE
+ */
+static int dbus_handle_resume(struct mbox_context *context,
+ struct mbox_dbus_msg *req,
+ struct mbox_dbus_msg *resp)
+{
+ int rc;
+
+ if (req->num_args != 1) {
+ return -E_DBUS_INVAL;
+ }
+
+ if (!(context->state & STATE_SUSPENDED)) {
+ /* We weren't suspended... */
+ return DBUS_SUCCESS;
+ }
+
+ if (req->args[0] == RESUME_FLASH_MODIFIED) {
+ /* Clear the bit and call the flash modified handler */
+ clr_bmc_events(context, BMC_EVENT_FLASH_CTRL_LOST,
+ NO_BMC_EVENT);
+ rc = dbus_handle_modified(context, req, resp);
+ } else {
+ /* Flash wasn't modified - just clear the bit with writeback */
+ rc = clr_bmc_events(context, BMC_EVENT_FLASH_CTRL_LOST,
+ SET_BMC_EVENT);
+ }
+
+ if (rc < 0) {
+ rc = -E_DBUS_HARDWARE;
+ }
+ context->state &= ~STATE_SUSPENDED;
+
+ return rc;
+}
+
+static const mboxd_dbus_handler dbus_handlers[NUM_DBUS_CMDS] = {
+ dbus_handle_ping,
+ dbus_handle_daemon_state,
+ dbus_handle_reset,
+ dbus_handle_suspend,
+ dbus_handle_resume,
+ dbus_handle_modified,
+ dbus_handle_kill,
+ dbus_handle_lpc_state
+};
+
+static int method_cmd(sd_bus_message *m, void *userdata,
+ sd_bus_error *ret_error)
+{
+ struct mbox_dbus_msg req = { 0 }, resp = { 0 };
+ struct mbox_context *context;
+ sd_bus_message *n;
+ int rc;
+
+ context = (struct mbox_context *) userdata;
+ if (!context) {
+ MSG_ERR("DBUS Internal Error\n");
+ rc = -E_DBUS_INTERNAL;
+ goto out;
+ }
+
+ /* Read the command */
+ rc = sd_bus_message_read(m, "y", &req.cmd);
+ if (rc < 0) {
+ MSG_ERR("DBUS error reading message: %s\n", strerror(-rc));
+ rc = -E_DBUS_INTERNAL;
+ goto out;
+ }
+
+ /* Read the args */
+ rc = sd_bus_message_read_array(m, 'y', (const void **) &req.args,
+ &req.num_args);
+ if (rc < 0) {
+ MSG_ERR("DBUS error reading message: %s\n", strerror(-rc));
+ rc = -E_DBUS_INTERNAL;
+ goto out;
+ }
+
+ /* Handle the command */
+ if (req.cmd >= NUM_DBUS_CMDS) {
+ rc = -E_DBUS_INVAL;
+ MSG_ERR("Received unknown dbus cmd: %d\n", req.cmd);
+ } else {
+ rc = dbus_handlers[req.cmd](context, &req, &resp);
+ }
+
+out:
+ if (rc < 0) {
+ resp.cmd = -rc;
+ }
+ sd_bus_message_new_method_return(m, &n); /* Generate response */
+ sd_bus_message_append(n, "y", resp.cmd); /* Set return code */
+ sd_bus_message_append_array(n, 'y', resp.args, resp.num_args);
+ sd_bus_send(sd_bus_message_get_bus(m), n, NULL); /* Send response */
+ free(resp.args);
+ return 0;
+}
+
+static const sd_bus_vtable mboxd_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+ SD_BUS_METHOD("cmd", "yay", "yay", &method_cmd,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_VTABLE_END
+};
+
+int init_mboxd_dbus(struct mbox_context *context)
+{
+ int rc;
+
+ rc = sd_bus_default_system(&context->bus);
+ if (rc < 0) {
+ MSG_ERR("Failed to connect to the system bus: %s\n",
+ strerror(-rc));
+ return rc;
+ }
+
+ rc = sd_bus_add_object_vtable(context->bus, NULL, DOBJ_NAME, DBUS_NAME,
+ mboxd_vtable, context);
+ if (rc < 0) {
+ MSG_ERR("Failed to register vtable: %s\n", strerror(-rc));
+ return rc;
+ }
+
+ rc = sd_bus_request_name(context->bus, DBUS_NAME,
+ SD_BUS_NAME_ALLOW_REPLACEMENT |
+ SD_BUS_NAME_REPLACE_EXISTING);
+ if (rc < 0) {
+ MSG_ERR("Failed to acquire service name: %s\n", strerror(-rc));
+ return rc;
+ }
+
+ rc = sd_bus_get_fd(context->bus);
+ if (rc < 0) {
+ MSG_ERR("Failed to get bus fd: %s\n", strerror(-rc));
+ return rc;
+ }
+
+ context->fds[DBUS_FD].fd = rc;
+
+ return 0;
+}
+
+void free_mboxd_dbus(struct mbox_context *context)
+{
+ sd_bus_unref(context->bus);
+}
diff --git a/mboxd_dbus.h b/mboxd_dbus.h
new file mode 100644
index 0000000..8dbac5e
--- /dev/null
+++ b/mboxd_dbus.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016 IBM
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef MBOXD_DBUS_H
+#define MBOXD_DBUS_H
+
+int init_mboxd_dbus(struct mbox_context *context);
+void free_mboxd_dbus(struct mbox_context *context);
+
+#endif /* MBOXD_DBUS_H */
diff --git a/mboxd_flash.c b/mboxd_flash.c
new file mode 100644
index 0000000..606f63d
--- /dev/null
+++ b/mboxd_flash.c
@@ -0,0 +1,278 @@
+/*
+ * Mailbox Daemon Flash Helpers
+ *
+ * Copyright 2016 IBM
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/timerfd.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <mtd/mtd-abi.h>
+
+#include "mbox.h"
+#include "common.h"
+#include "mboxd_flash.h"
+
+int init_flash_dev(struct mbox_context *context)
+{
+ char *filename = get_dev_mtd();
+ int fd, rc = 0;
+
+ if (!filename) {
+ MSG_ERR("Couldn't find the PNOR /dev/mtd partition\n");
+ return -1;
+ }
+
+ MSG_OUT("Opening %s\n", filename);
+
+ /* Open Flash Device */
+ fd = open(filename, O_RDWR);
+ if (fd < 0) {
+ MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n",
+ filename, strerror(errno));
+ rc = -errno;
+ goto out;
+ }
+ context->fds[MTD_FD].fd = fd;
+
+ /* Read the Flash Info */
+ if (ioctl(fd, MEMGETINFO, &context->mtd_info) == -1) {
+ MSG_ERR("Couldn't get information about MTD: %s\n",
+ strerror(errno));
+ rc = -1;
+ goto out;
+ }
+
+ /* We know the erase size so we can allocate the flash_erased bytemap */
+ context->erase_size_shift = log_2(context->mtd_info.erasesize);
+ context->flash_bmap = calloc(context->flash_size >>
+ context->erase_size_shift,
+ sizeof(*context->flash_bmap));
+
+out:
+ free(filename);
+ return rc;
+}
+
+void free_flash_dev(struct mbox_context *context)
+{
+ free(context->flash_bmap);
+ close(context->fds[MTD_FD].fd);
+}
+
+/* Flash Functions */
+
+#define CHUNKSIZE (64 * 1024)
+
+/*
+ * copy_flash() - Copy data from the flash device into a provided buffer
+ * @context: The mbox context pointer
+ * @offset: The flash offset to copy from (bytes)
+ * @mem: The buffer to copy into (must be of atleast size)
+ * @size: The number of bytes to copy
+ *
+ * Return: 0 on success otherwise negative error code
+ */
+int copy_flash(struct mbox_context *context, uint32_t offset, void *mem,
+ uint32_t size)
+{
+ MSG_OUT("Loading flash at %p for 0x%08x bytes from offset 0x%.8x\n",
+ mem, size, offset);
+ if (lseek(context->fds[MTD_FD].fd, offset, SEEK_SET) != offset) {
+ MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset,
+ strerror(errno));
+ return -MBOX_R_SYSTEM_ERROR;
+ }
+
+ while (size) {
+ uint32_t size_read = read(context->fds[MTD_FD].fd, mem,
+ min_u32(CHUNKSIZE, size));
+ if (size_read < 0) {
+ MSG_ERR("Couldn't copy mtd into ram: %d. %s\n",
+ size_read, strerror(size_read));
+ return -MBOX_R_SYSTEM_ERROR;
+ }
+
+ size -= size_read;
+ mem += size_read;
+ }
+
+ return 0;
+}
+
+/*
+ * flash_is_erased() - Check if an offset into flash is erased
+ * @context: The mbox context pointer
+ * @offset: The flash offset to check (bytes)
+ *
+ * Return: true if erased otherwise false
+ */
+static inline bool flash_is_erased(struct mbox_context *context,
+ uint32_t offset)
+{
+ return context->flash_bmap[offset >> context->erase_size_shift]
+ == FLASH_ERASED;
+}
+
+/*
+ * set_flash_bytemap() - Set the flash erased bytemap
+ * @context: The mbox context pointer
+ * @offset: The flash offset to set (bytes)
+ * @count: Number of bytes to set
+ * @val: Value to set the bytemap to
+ *
+ * The flash bytemap only tracks the erased status at the erase block level so
+ * this will update the erased state for an (or many) erase blocks
+ *
+ * Return: 0 if success otherwise negative error code
+ */
+int set_flash_bytemap(struct mbox_context *context, uint32_t offset,
+ uint32_t count, uint8_t val)
+{
+ if ((offset + count) > context->flash_size) {
+ return -MBOX_R_PARAM_ERROR;
+ }
+
+ memset(context->flash_bmap + (offset >> context->erase_size_shift),
+ val,
+ align_up(count, 1 << context->erase_size_shift) >>
+ context->erase_size_shift);
+
+ return 0;
+}
+
+/*
+ * erase_flash() - Erase the flash
+ * @context: The mbox context pointer
+ * @offset: The flash offset to erase (bytes)
+ * @size: The number of bytes to erase
+ *
+ * Return: 0 on success otherwise negative error code
+ */
+int erase_flash(struct mbox_context *context, uint32_t offset, uint32_t count)
+{
+ const uint32_t erase_size = 1 << context->erase_size_shift;
+ struct erase_info_user erase_info = { 0 };
+ int rc;
+
+ MSG_OUT("Erasing 0x%.8x for 0x%.8x\n", offset, count);
+
+ /*
+ * We have an erased_bytemap for the flash so we want to avoid erasing
+ * blocks which we already know to be erased. Look for runs of blocks
+ * which aren't erased and erase the entire run at once to avoid how
+ * often we have to call the erase ioctl. If the block is already
+ * erased then there's nothing we need to do.
+ */
+ while (count) {
+ if (!flash_is_erased(context, offset)) { /* Need to erase */
+ if (!erase_info.length) { /* Start of not-erased run */
+ erase_info.start = offset;
+ }
+ erase_info.length += erase_size;
+ } else if (erase_info.length) { /* Already erased|end of run? */
+ /* Erase the previous run which just ended */
+ rc = ioctl(context->fds[MTD_FD].fd, MEMERASE,
+ &erase_info);
+ if (rc < 0) {
+ MSG_ERR("Couldn't erase flash at 0x%.8x\n",
+ erase_info.start);
+ return -MBOX_R_SYSTEM_ERROR;
+ }
+ /* Mark ERASED where we just erased */
+ set_flash_bytemap(context, erase_info.start,
+ erase_info.length, FLASH_ERASED);
+ erase_info.start = 0;
+ erase_info.length = 0;
+ }
+
+ offset += erase_size;
+ count -= erase_size;
+ }
+
+ if (erase_info.length) {
+ rc = ioctl(context->fds[MTD_FD].fd, MEMERASE, &erase_info);
+ if (rc < 0) {
+ MSG_ERR("Couldn't erase flash at 0x%.8x\n",
+ erase_info.start);
+ return -MBOX_R_SYSTEM_ERROR;
+ }
+ /* Mark ERASED where we just erased */
+ set_flash_bytemap(context, erase_info.start, erase_info.length,
+ FLASH_ERASED);
+ }
+
+ return 0;
+}
+
+/*
+ * write_flash() - Write the flash from a provided buffer
+ * @context: The mbox context pointer
+ * @offset: The flash offset to write to (bytes)
+ * @buf: The buffer to write from (must be of atleast size)
+ * @size: The number of bytes to write
+ *
+ * Return: 0 on success otherwise negative error code
+ */
+int write_flash(struct mbox_context *context, uint32_t offset, void *buf,
+ uint32_t count)
+{
+ uint32_t buf_offset = 0;
+ int rc;
+
+ MSG_OUT("Writing 0x%.8x for 0x%.8x from %p\n", offset, count, buf);
+
+ if (lseek(context->fds[MTD_FD].fd, offset, SEEK_SET) != offset) {
+ MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset,
+ strerror(errno));
+ return -MBOX_R_SYSTEM_ERROR;
+ }
+
+ while (count) {
+ rc = write(context->fds[MTD_FD].fd, buf + buf_offset, count);
+ if (rc < 0) {
+ MSG_ERR("Couldn't write to flash, write lost: %s\n",
+ strerror(errno));
+ return -MBOX_R_WRITE_ERROR;
+ }
+ /* Mark *NOT* erased where we just wrote */
+ set_flash_bytemap(context, offset + buf_offset, rc,
+ FLASH_DIRTY);
+ count -= rc;
+ buf_offset += rc;
+ }
+
+ return 0;
+}
diff --git a/mboxd_flash.h b/mboxd_flash.h
new file mode 100644
index 0000000..69bc121
--- /dev/null
+++ b/mboxd_flash.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 IBM
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef MBOXD_FLASH_H
+#define MBOXD_FLASH_H
+
+#define FLASH_DIRTY 0x00
+#define FLASH_ERASED 0x01
+
+int init_flash_dev(struct mbox_context *context);
+void free_flash_dev(struct mbox_context *context);
+int copy_flash(struct mbox_context *context, uint32_t offset, void *mem,
+ uint32_t size);
+int set_flash_bytemap(struct mbox_context *context, uint32_t offset,
+ uint32_t count, uint8_t val);
+int erase_flash(struct mbox_context *context, uint32_t offset, uint32_t count);
+int write_flash(struct mbox_context *context, uint32_t offset, void *buf,
+ uint32_t count);
+
+#endif /* MBOXD_FLASH_H */
diff --git a/mboxd_lpc.c b/mboxd_lpc.c
new file mode 100644
index 0000000..42e4328
--- /dev/null
+++ b/mboxd_lpc.c
@@ -0,0 +1,192 @@
+/*
+ * Mailbox Daemon LPC Helpers
+ *
+ * Copyright 2016 IBM
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/timerfd.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "mbox.h"
+#include "common.h"
+#include "mboxd_lpc.h"
+#include "mboxd_flash.h"
+#include <linux/aspeed-lpc-ctrl.h>
+
+#define LPC_CTRL_PATH "/dev/aspeed-lpc-ctrl"
+
+int init_lpc_dev(struct mbox_context *context)
+{
+ struct aspeed_lpc_ctrl_mapping map = {
+ .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY,
+ .window_id = 0, /* There's only one */
+ .flags = 0,
+ .addr = 0,
+ .offset = 0,
+ .size = 0
+ };
+ int fd;
+
+ /* Open LPC Device */
+ MSG_OUT("Opening %s\n", LPC_CTRL_PATH);
+ fd = open(LPC_CTRL_PATH, O_RDWR | O_SYNC);
+ if (fd < 0) {
+ MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n",
+ LPC_CTRL_PATH, strerror(errno));
+ return -errno;
+ }
+
+ context->fds[LPC_CTRL_FD].fd = fd;
+
+ /* Find Size of Reserved Memory Region */
+ MSG_OUT("Getting buffer size...\n");
+ if (ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map) < 0) {
+ MSG_ERR("Couldn't get lpc control buffer size: %s\n",
+ strerror(errno));
+ return -errno;
+ }
+
+ context->mem_size = map.size;
+ /* Map at the top of the 28-bit LPC firmware address space-0 */
+ context->lpc_base = 0x0FFFFFFF & -context->mem_size;
+
+ /* mmap the Reserved Memory Region */
+ MSG_OUT("Mapping %s for %u\n", LPC_CTRL_PATH, context->mem_size);
+ context->mem = mmap(NULL, context->mem_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (context->mem == MAP_FAILED) {
+ MSG_ERR("Didn't manage to mmap %s: %s\n", LPC_CTRL_PATH,
+ strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
+
+void free_lpc_dev(struct mbox_context *context)
+{
+ if (context->mem) {
+ munmap(context->mem, context->mem_size);
+ }
+ close(context->fds[LPC_CTRL_FD].fd);
+}
+
+/*
+ * point_to_flash() - Point the lpc bus mapping to the actual flash device
+ * @context: The mbox context pointer
+ *
+ * Return: 0 on success otherwise negative error code
+ */
+int point_to_flash(struct mbox_context *context)
+{
+ struct aspeed_lpc_ctrl_mapping map = {
+ .window_type = ASPEED_LPC_CTRL_WINDOW_FLASH,
+ .window_id = 0, /* Theres only one */
+ .flags = 0,
+ /*
+ * The mask is because the top nibble is the host LPC FW space,
+ * we want space 0.
+ */
+ .addr = 0x0FFFFFFF & -context->flash_size,
+ .offset = 0,
+ .size = context->flash_size
+ };
+
+ if (context->state & MAPS_FLASH) {
+ return 0; /* LPC Bus already points to flash */
+ }
+ /* Don't let the host access flash while we're suspended */
+ if (context->state & STATE_SUSPENDED) {
+ MSG_ERR("Can't point lpc mapping to flash while suspended\n");
+ return -MBOX_R_PARAM_ERROR;
+ }
+
+ MSG_OUT("Pointing HOST LPC bus at the actual flash\n");
+ MSG_OUT("Assuming %dMB of flash: HOST LPC 0x%08x\n",
+ context->flash_size >> 20, map.addr);
+
+ if (ioctl(context->fds[LPC_CTRL_FD].fd, ASPEED_LPC_CTRL_IOCTL_MAP, &map)
+ == -1) {
+ MSG_ERR("Failed to point the LPC BUS at the actual flash: %s\n",
+ strerror(errno));
+ return -MBOX_R_SYSTEM_ERROR;
+ }
+
+ context->state = ACTIVE_MAPS_FLASH;
+ /*
+ * Since the host now has access to the flash it can change it out from
+ * under us
+ */
+ return set_flash_bytemap(context, 0, context->flash_size, FLASH_DIRTY);
+}
+
+/*
+ * point_to_memory() - Point the lpc bus mapping to the reserved memory region
+ * @context: The mbox context pointer
+ *
+ * Return: 0 on success otherwise negative error code
+ */
+int point_to_memory(struct mbox_context *context)
+{
+ struct aspeed_lpc_ctrl_mapping map = {
+ .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY,
+ .window_id = 0, /* There's only one */
+ .flags = 0,
+ .addr = context->lpc_base,
+ .offset = 0,
+ .size = context->mem_size
+ };
+
+ if (context->state & MAPS_MEM) {
+ return 0; /* LPC Bus already points to reserved memory area */
+ }
+
+ MSG_OUT("Pointing HOST LPC bus at memory region %p of size 0x%.8x\n",
+ context->mem, context->mem_size);
+ MSG_OUT("LPC address 0x%.8x\n", map.addr);
+
+ if (ioctl(context->fds[LPC_CTRL_FD].fd, ASPEED_LPC_CTRL_IOCTL_MAP,
+ &map)) {
+ MSG_ERR("Failed to point the LPC BUS to memory: %s\n",
+ strerror(errno));
+ return -MBOX_R_SYSTEM_ERROR;
+ }
+
+ /* LPC now maps memory (keep suspended state) */
+ context->state = MAPS_MEM | (context->state & STATE_SUSPENDED);
+
+ return 0;
+}
diff --git a/mboxd_lpc.h b/mboxd_lpc.h
new file mode 100644
index 0000000..bb288a9
--- /dev/null
+++ b/mboxd_lpc.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2016 IBM
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef MBOXD_LPC_H
+#define MBOXD_LPC_H
+
+int init_lpc_dev(struct mbox_context *context);
+void free_lpc_dev(struct mbox_context *context);
+int point_to_flash(struct mbox_context *context);
+int point_to_memory(struct mbox_context *context);
+
+#endif /* MBOXD_LPC_H */
diff --git a/mboxd_msg.c b/mboxd_msg.c
new file mode 100644
index 0000000..179af60
--- /dev/null
+++ b/mboxd_msg.c
@@ -0,0 +1,802 @@
+/*
+ * Mailbox Daemon MBOX Message Helpers
+ *
+ * Copyright 2016 IBM
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/timerfd.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "mbox.h"
+#include "common.h"
+#include "mboxd_msg.h"
+#include "mboxd_windows.h"
+#include "mboxd_lpc.h"
+
+static int mbox_handle_flush_window(struct mbox_context *context, union mbox_regs *req,
+ struct mbox_msg *resp);
+
+typedef int (*mboxd_mbox_handler)(struct mbox_context *, union mbox_regs *,
+ struct mbox_msg *);
+
+/*
+ * write_bmc_event_reg() - Write to the BMC controlled status register (reg 15)
+ * @context: The mbox context pointer
+ *
+ * Return: 0 on success otherwise negative error code
+ */
+static int write_bmc_event_reg(struct mbox_context *context)
+{
+ int rc;
+
+ /* Seek mbox registers */
+ rc = lseek(context->fds[MBOX_FD].fd, MBOX_BMC_EVENT, SEEK_SET);
+ if (rc != MBOX_BMC_EVENT) {
+ MSG_ERR("Couldn't lseek mbox to byte %d: %s\n", MBOX_BMC_EVENT,
+ strerror(errno));
+ return -MBOX_R_SYSTEM_ERROR;
+ }
+
+ /* Write to mbox status register */
+ rc = write(context->fds[MBOX_FD].fd, &context->bmc_events, 1);
+ if (rc != 1) {
+ MSG_ERR("Couldn't write to BMC status reg: %s\n",
+ strerror(errno));
+ return -MBOX_R_SYSTEM_ERROR;
+ }
+
+ /* Reset to start */
+ rc = lseek(context->fds[MBOX_FD].fd, 0, SEEK_SET);
+ if (rc) {
+ MSG_ERR("Couldn't reset MBOX offset to zero: %s\n",
+ strerror(errno));
+ return -MBOX_R_SYSTEM_ERROR;
+ }
+
+ return 0;
+}
+
+/*
+ * set_bmc_events() - Set BMC events
+ * @context: The mbox context pointer
+ * @bmc_event: The bits to set
+ * @write_back: Whether to write back to the register -> will interrupt host
+ *
+ * Return: 0 on success otherwise negative error code
+ */
+int set_bmc_events(struct mbox_context *context, uint8_t bmc_event,
+ bool write_back)
+{
+ uint8_t mask = 0x00;
+
+ switch (context->version) {
+ case API_VERSION_1:
+ mask = BMC_EVENT_V1_MASK;
+ break;
+ default:
+ mask = BMC_EVENT_V2_MASK;
+ break;
+ }
+
+ context->bmc_events |= (bmc_event & mask);
+
+ return write_back ? write_bmc_event_reg(context) : 0;
+}
+
+/*
+ * clr_bmc_events() - Clear BMC events
+ * @context: The mbox context pointer
+ * @bmc_event: The bits to clear
+ * @write_back: Whether to write back to the register -> will interrupt host
+ *
+ * Return: 0 on success otherwise negative error code
+ */
+int clr_bmc_events(struct mbox_context *context, uint8_t bmc_event,
+ bool write_back)
+{
+ context->bmc_events &= ~bmc_event;
+
+ return write_back ? write_bmc_event_reg(context) : 0;
+}
+
+/* Command Handlers */
+
+/*
+ * Command: RESET_STATE
+ * Reset the LPC mapping to point back at the flash
+ */
+static int mbox_handle_reset(struct mbox_context *context,
+ union mbox_regs *req, struct mbox_msg *resp)
+{
+ /* Host requested it -> No BMC Event */
+ reset_all_windows(context, NO_BMC_EVENT);
+ return point_to_flash(context);
+}
+
+/*
+ * Command: GET_MBOX_INFO
+ * Get the API version, default window size and block size
+ * We also set the LPC mapping to point to the reserved memory region here so
+ * this command must be called before any window manipulation
+ *
+ * V1:
+ * ARGS[0]: API Version
+ *
+ * RESP[0]: API Version
+ * RESP[1:2]: Default read window size (number of blocks)
+ * RESP[3:4]: Default write window size (number of blocks)
+ * RESP[5]: Block size (as shift)
+ *
+ * V2:
+ * ARGS[0]: API Version
+ *
+ * RESP[0]: API Version
+ * RESP[1:2]: Default read window size (number of blocks)
+ * RESP[3:4]: Default write window size (number of blocks)
+ * RESP[5]: Block size (as shift)
+ */
+static int mbox_handle_mbox_info(struct mbox_context *context,
+ union mbox_regs *req, struct mbox_msg *resp)
+{
+ uint8_t mbox_api_version = req->msg.args[0];
+ uint8_t old_api_version = context->version;
+ int rc;
+
+ /* Check we support the version requested */
+ if (mbox_api_version < API_MIN_VERSION ||
+ mbox_api_version > API_MAX_VERSION) {
+ return -MBOX_R_PARAM_ERROR;
+ }
+ context->version = mbox_api_version;
+ MSG_OUT("Using Protocol Version: %d\n", context->version);
+
+ /*
+ * The reset state is currently to have the LPC bus point directly to
+ * flash, since we got a mbox_info command we know the host can talk
+ * mbox so point the LPC bus mapping to the reserved memory region now
+ * so the host can access what we put in it.
+ */
+ rc = point_to_memory(context);
+ if (rc < 0) {
+ return rc;
+ }
+
+ switch (context->version) {
+ case API_VERSION_1:
+ context->block_size_shift = BLOCK_SIZE_SHIFT_V1;
+ break;
+ default:
+ context->block_size_shift = log_2(context->mtd_info.erasesize);
+ break;
+ }
+ MSG_OUT("Block Size Shift: %d\n", context->block_size_shift);
+
+ /* Now we know the blocksize we can allocate the window dirty_bytemap */
+ if (mbox_api_version != old_api_version) {
+ alloc_window_dirty_bytemap(context);
+ }
+ /* Reset if we were V1 since this required exact window mapping */
+ if (old_api_version == API_VERSION_1) {
+ /*
+ * This will only set the BMC event if there was a current
+ * window -> In which case we are better off notifying the
+ * host.
+ */
+ reset_all_windows(context, SET_BMC_EVENT);
+ }
+
+ resp->args[0] = mbox_api_version;
+ if (context->version == API_VERSION_1) {
+ put_u16(&resp->args[1], context->windows.default_size >>
+ context->block_size_shift);
+ put_u16(&resp->args[3], context->windows.default_size >>
+ context->block_size_shift);
+ }
+ if (context->version >= API_VERSION_2) {
+ resp->args[5] = context->block_size_shift;
+ }
+
+ return 0;
+}
+
+/*
+ * Command: GET_FLASH_INFO
+ * Get the flash size and erase granularity
+ *
+ * V1:
+ * RESP[0:3]: Flash Size (bytes)
+ * RESP[4:7]: Erase Size (bytes)
+ * V2:
+ * RESP[0:1]: Flash Size (number of blocks)
+ * RESP[2:3]: Erase Size (number of blocks)
+ */
+static int mbox_handle_flash_info(struct mbox_context *context,
+ union mbox_regs *req, struct mbox_msg *resp)
+{
+ switch (context->version) {
+ case API_VERSION_1:
+ /* Both Sizes in Bytes */
+ put_u32(&resp->args[0], context->flash_size);
+ put_u32(&resp->args[4], context->mtd_info.erasesize);
+ break;
+ case API_VERSION_2:
+ /* Both Sizes in Block Size */
+ put_u16(&resp->args[0],
+ context->flash_size >> context->block_size_shift);
+ put_u16(&resp->args[2],
+ context->mtd_info.erasesize >>
+ context->block_size_shift);
+ break;
+ default:
+ MSG_ERR("API Version Not Valid - Invalid System State\n");
+ return -MBOX_R_SYSTEM_ERROR;
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * get_lpc_addr_shifted() - Get lpc address of the current window
+ * @context: The mbox context pointer
+ *
+ * Return: The lpc address to access that offset shifted by block size
+ */
+static inline uint16_t get_lpc_addr_shifted(struct mbox_context *context)
+{
+ uint32_t lpc_addr, mem_offset;
+
+ /* Offset of the current window in the reserved memory region */
+ mem_offset = context->current->mem - context->mem;
+ /* Total LPC Address */
+ lpc_addr = context->lpc_base + mem_offset;
+
+ return lpc_addr >> context->block_size_shift;
+}
+
+/*
+ * Command: CREATE_READ_WINDOW
+ * Opens a read window
+ * First checks if any current window with the requested data, if so we just
+ * point the host to that. Otherwise we read the request data in from flash and
+ * point the host there.
+ *
+ * V1:
+ * ARGS[0:1]: Window Location as Offset into Flash (number of blocks)
+ *
+ * RESP[0:1]: LPC bus address for host to access this window (number of blocks)
+ *
+ * V2:
+ * ARGS[0:1]: Window Location as Offset into Flash (number of blocks)
+ * ARGS[2:3]: Requested window size (number of blocks)
+ *
+ * RESP[0:1]: LPC bus address for host to access this window (number of blocks)
+ * RESP[2:3]: Actual window size that the host can access (number of blocks)
+ */
+static int mbox_handle_read_window(struct mbox_context *context,
+ union mbox_regs *req, struct mbox_msg *resp)
+{
+ uint32_t flash_offset;
+ int rc;
+
+ /* Close the current window if there is one */
+ if (context->current) {
+ /* There is an implicit flush if it was a write window */
+ if (context->current_is_write) {
+ rc = mbox_handle_flush_window(context, NULL, NULL);
+ if (rc < 0) {
+ MSG_ERR("Couldn't Flush Write Window\n");
+ return rc;
+ }
+ }
+ close_current_window(context, NO_BMC_EVENT, FLAGS_NONE);
+ }
+
+ /* Offset the host has requested */
+ flash_offset = get_u16(&req->msg.args[0]) << context->block_size_shift;
+ MSG_OUT("Host Requested Flash @ 0x%.8x\n", flash_offset);
+ /* Check if we have an existing window */
+ context->current = search_windows(context, flash_offset,
+ context->version == API_VERSION_1);
+
+ if (!context->current) { /* No existing window */
+ rc = create_map_window(context, &context->current, flash_offset,
+ context->version == API_VERSION_1);
+ if (rc < 0) { /* Unable to map offset */
+ MSG_ERR("Couldn't create window mapping for offset 0x%.8x\n"
+ , flash_offset);
+ return rc;
+ }
+ }
+
+ put_u16(&resp->args[0], get_lpc_addr_shifted(context));
+ if (context->version >= API_VERSION_2) {
+ put_u16(&resp->args[2], context->current->size >>
+ context->block_size_shift);
+ put_u16(&resp->args[4], context->current->flash_offset >>
+ context->block_size_shift);
+ }
+
+ context->current_is_write = false;
+
+ return 0;
+}
+
+/*
+ * Command: CREATE_WRITE_WINDOW
+ * Opens a write window
+ * First checks if any current window with the requested data, if so we just
+ * point the host to that. Otherwise we read the request data in from flash and
+ * point the host there.
+ *
+ * V1:
+ * ARGS[0:1]: Window Location as Offset into Flash (number of blocks)
+ *
+ * RESP[0:1]: LPC bus address for host to access this window (number of blocks)
+ *
+ * V2:
+ * ARGS[0:1]: Window Location as Offset into Flash (number of blocks)
+ * ARGS[2:3]: Requested window size (number of blocks)
+ *
+ * RESP[0:1]: LPC bus address for host to access this window (number of blocks)
+ * RESP[2:3]: Actual window size that was mapped/host can access (n.o. blocks)
+ */
+static int mbox_handle_write_window(struct mbox_context *context,
+ union mbox_regs *req, struct mbox_msg *resp)
+{
+ int rc;
+
+ /*
+ * This is very similar to opening a read window (exactly the same
+ * for now infact)
+ */
+ rc = mbox_handle_read_window(context, req, resp);
+ if (rc < 0) {
+ return rc;
+ }
+
+ context->current_is_write = true;
+ return rc;
+}
+
+/*
+ * Commands: MARK_WRITE_DIRTY
+ * Marks a portion of the current (write) window dirty, informing the daemon
+ * that is has been written to and thus must be at some point written to the
+ * backing store
+ * These changes aren't written back to the backing store unless flush is then
+ * called or the window closed
+ *
+ * V1:
+ * ARGS[0:1]: Where within flash to start (number of blocks)
+ * ARGS[2:5]: Number to mark dirty (number of bytes)
+ *
+ * V2:
+ * ARGS[0:1]: Where within window to start (number of blocks)
+ * ARGS[2:3]: Number to mark dirty (number of blocks)
+ */
+static int mbox_handle_dirty_window(struct mbox_context *context,
+ union mbox_regs *req, struct mbox_msg *resp)
+{
+ uint32_t offset, size;
+
+ if (!(context->current && context->current_is_write)) {
+ MSG_ERR("Tried to call mark dirty without open write window\n");
+ return context->version >= API_VERSION_2 ? -MBOX_R_WINDOW_ERROR
+ : -MBOX_R_PARAM_ERROR;
+ }
+
+ offset = get_u16(&req->msg.args[0]);
+
+ if (context->version >= API_VERSION_2) {
+ size = get_u16(&req->msg.args[2]);
+ } else {
+ uint32_t off;
+ /* For V1 offset given relative to flash - we want the window */
+ off = offset - ((context->current->flash_offset) >>
+ context->block_size_shift);
+ if (off > offset) { /* Underflow - before current window */
+ MSG_ERR("Tried to mark dirty before start of window\n");
+ MSG_ERR("requested offset: 0x%x window start: 0x%x\n",
+ offset << context->block_size_shift,
+ context->current->flash_offset);
+ return -MBOX_R_PARAM_ERROR;
+ }
+ offset = off;
+ size = get_u32(&req->msg.args[2]);
+ /*
+ * We only track dirty at the block level.
+ * For protocol V1 we can get away with just marking the whole
+ * block dirty.
+ */
+ size = align_up(size, 1 << context->block_size_shift);
+ size >>= context->block_size_shift;
+ }
+
+ return set_window_bytemap(context, context->current, offset, size,
+ WINDOW_DIRTY);
+}
+
+/*
+ * Commands: MARK_WRITE_ERASE
+ * Erases a portion of the current window
+ * These changes aren't written back to the backing store unless flush is then
+ * called or the window closed
+ *
+ * V1:
+ * Unimplemented
+ *
+ * V2:
+ * ARGS[0:1]: Where within window to start (number of blocks)
+ * ARGS[2:3]: Number to erase (number of blocks)
+ */
+static int mbox_handle_erase_window(struct mbox_context *context,
+ union mbox_regs *req, struct mbox_msg *resp)
+{
+ uint32_t offset, size;
+ int rc;
+
+ if (context->version < API_VERSION_2) {
+ MSG_ERR("Protocol Version invalid for Erase Command\n");
+ return -MBOX_R_PARAM_ERROR;
+ }
+
+ if (!(context->current && context->current_is_write)) {
+ MSG_ERR("Tried to call erase without open write window\n");
+ return -MBOX_R_WINDOW_ERROR;
+ }
+
+ offset = get_u16(&req->msg.args[0]);
+ size = get_u16(&req->msg.args[2]);
+
+ rc = set_window_bytemap(context, context->current, offset, size,
+ WINDOW_ERASED);
+ if (rc < 0) {
+ return rc;
+ }
+
+ /* Write 0xFF to mem -> This ensures consistency between flash & ram */
+ memset(context->current->mem + (offset << context->block_size_shift),
+ 0xFF, size << context->block_size_shift);
+
+ return 0;
+}
+
+/*
+ * Command: WRITE_FLUSH
+ * Flushes any dirty or erased blocks in the current window back to the backing
+ * store
+ * NOTE: For V1 this behaves much the same as the dirty command in that it
+ * takes an offset and number of blocks to dirty, then also performs a flush as
+ * part of the same command. For V2 this will only flush blocks already marked
+ * dirty/erased with the appropriate commands and doesn't take any arguments
+ * directly.
+ *
+ * V1:
+ * ARGS[0:1]: Where within window to start (number of blocks)
+ * ARGS[2:5]: Number to mark dirty (number of bytes)
+ *
+ * V2:
+ * NONE
+ */
+static int mbox_handle_flush_window(struct mbox_context *context,
+ union mbox_regs *req, struct mbox_msg *resp)
+{
+ int rc, i, offset, count;
+ uint8_t prev;
+
+ if (!(context->current && context->current_is_write)) {
+ MSG_ERR("Tried to call flush without open write window\n");
+ return context->version >= API_VERSION_2 ? -MBOX_R_WINDOW_ERROR
+ : -MBOX_R_PARAM_ERROR;
+ }
+
+ /*
+ * For V1 the Flush command acts much the same as the dirty command
+ * except with a flush as well. Only do this on an actual flush
+ * command not when we call flush because we've implicitly closed a
+ * window because we might not have the required args in req.
+ */
+ if (context->version == API_VERSION_1 && req &&
+ req->msg.command == MBOX_C_WRITE_FLUSH) {
+ rc = mbox_handle_dirty_window(context, req, NULL);
+ if (rc < 0) {
+ return rc;
+ }
+ }
+
+ offset = 0;
+ count = 0;
+ prev = WINDOW_CLEAN;
+
+ /*
+ * We look for streaks of the same type and keep a count, when the type
+ * (dirty/erased) changes we perform the required action on the backing
+ * store and update the current streak-type
+ */
+ for (i = 0; i < (context->current->size >> context->block_size_shift);
+ i++) {
+ uint8_t cur = context->current->dirty_bmap[i];
+ if (cur != WINDOW_CLEAN) {
+ if (cur == prev) { /* Same as previous block, incrmnt */
+ count++;
+ } else if (prev == WINDOW_CLEAN) { /* Start of run */
+ offset = i;
+ count++;
+ } else { /* Change in streak type */
+ rc = write_from_window(context, offset, count,
+ prev);
+ if (rc < 0) {
+ return rc;
+ }
+ offset = i;
+ count = 1;
+ }
+ } else {
+ if (prev != WINDOW_CLEAN) { /* End of a streak */
+ rc = write_from_window(context, offset, count,
+ prev);
+ if (rc < 0) {
+ return rc;
+ }
+ offset = 0;
+ count = 0;
+ }
+ }
+ prev = cur;
+ }
+
+ if (prev != WINDOW_CLEAN) { /* Still the last streak to write */
+ rc = write_from_window(context, offset, count, prev);
+ if (rc < 0) {
+ return rc;
+ }
+ }
+
+ /* Clear the dirty bytemap since we have written back all changes */
+ return set_window_bytemap(context, context->current, 0,
+ context->current->size >>
+ context->block_size_shift,
+ WINDOW_CLEAN);
+}
+
+/*
+ * Command: CLOSE_WINDOW
+ * Close the current window
+ * NOTE: There is an implicit flush
+ *
+ * V1:
+ * NONE
+ *
+ * V2:
+ * ARGS[0]: FLAGS
+ */
+static int mbox_handle_close_window(struct mbox_context *context,
+ union mbox_regs *req, struct mbox_msg *resp)
+{
+ uint8_t flags = 0;
+ int rc;
+
+ /* Close the current window if there is one */
+ if (context->current) {
+ /* There is an implicit flush if it was a write window */
+ if (context->current_is_write) {
+ rc = mbox_handle_flush_window(context, NULL, NULL);
+ if (rc < 0) {
+ MSG_ERR("Couldn't Flush Write Window\n");
+ return rc;
+ }
+ }
+
+ if (context->version >= API_VERSION_2) {
+ flags = req->msg.args[0];
+ }
+
+ /* Host asked for it -> Don't set the BMC Event */
+ close_current_window(context, NO_BMC_EVENT, flags);
+ }
+
+ return 0;
+}
+
+/*
+ * Command: BMC_EVENT_ACK
+ * Sent by the host to acknowledge BMC events supplied in mailbox register 15
+ *
+ * ARGS[0]: Bitmap of bits to ack (by clearing)
+ */
+static int mbox_handle_ack(struct mbox_context *context, union mbox_regs *req,
+ struct mbox_msg *resp)
+{
+ uint8_t bmc_events = req->msg.args[0];
+
+ return clr_bmc_events(context, (bmc_events & BMC_EVENT_ACK_MASK),
+ SET_BMC_EVENT);
+}
+
+/*
+ * check_cmd_valid() - Check if the given command is a valid mbox command code
+ * @context: The mbox context pointer
+ * @cmd: The command code
+ *
+ * Return: 0 if command is valid otherwise negative error code
+ */
+static int check_cmd_valid(struct mbox_context *context, int cmd)
+{
+ if (cmd <= 0 || cmd > NUM_MBOX_CMDS) {
+ MSG_ERR("UNKNOWN MBOX COMMAND: %d\n", cmd);
+ return -MBOX_R_PARAM_ERROR;
+ }
+ if (context->state & STATE_SUSPENDED) {
+ if (cmd != MBOX_C_GET_MBOX_INFO && cmd != MBOX_C_ACK) {
+ MSG_ERR("Cannot use that cmd while suspended: %d\n",
+ cmd);
+ return context->version >= API_VERSION_2 ? -MBOX_R_BUSY
+ : -MBOX_R_PARAM_ERROR;
+ }
+ }
+ if (!(context->state & MAPS_MEM)) {
+ if (cmd != MBOX_C_RESET_STATE && cmd != MBOX_C_GET_MBOX_INFO
+ && cmd != MBOX_C_ACK) {
+ MSG_ERR("Must call GET_MBOX_INFO before that cmd: %d\n",
+ cmd);
+ return -MBOX_R_PARAM_ERROR;
+ }
+ }
+
+ return 0;
+}
+
+static const mboxd_mbox_handler mbox_handlers[] = {
+ mbox_handle_reset,
+ mbox_handle_mbox_info,
+ mbox_handle_flash_info,
+ mbox_handle_read_window,
+ mbox_handle_close_window,
+ mbox_handle_write_window,
+ mbox_handle_dirty_window,
+ mbox_handle_flush_window,
+ mbox_handle_ack,
+ mbox_handle_erase_window
+};
+
+/*
+ * handle_mbox_req() - Handle an incoming mbox command request
+ * @context: The mbox context pointer
+ * @req: The mbox request message
+ *
+ * Return: 0 if handled successfully otherwise negative error code
+ */
+static int handle_mbox_req(struct mbox_context *context, union mbox_regs *req)
+{
+ struct mbox_msg resp = {
+ .command = req->msg.command,
+ .seq = req->msg.seq,
+ .args = { 0 },
+ .response = MBOX_R_SUCCESS
+ };
+ int rc = 0, len;
+
+ MSG_OUT("Got data in with command %d\n", req->msg.command);
+ rc = check_cmd_valid(context, req->msg.command);
+ if (rc < 0) {
+ resp.response = -rc;
+ } else {
+ /* Commands start at 1 so we have to subtract 1 from the cmd */
+ rc = mbox_handlers[req->msg.command - 1](context, req, &resp);
+ if (rc < 0) {
+ MSG_ERR("Error handling mbox cmd: %d\n",
+ req->msg.command);
+ resp.response = -rc;
+ }
+ }
+
+ MSG_OUT("Writing response to MBOX regs: %d\n", resp.response);
+ len = write(context->fds[MBOX_FD].fd, &resp, sizeof(resp));
+ if (len < sizeof(resp)) {
+ MSG_ERR("Didn't write the full response\n");
+ rc = -errno;
+ }
+
+ return rc;
+}
+
+/*
+ * get_message() - Read an mbox request message from the mbox registers
+ * @context: The mbox context pointer
+ * @msg: Where to put the received message
+ *
+ * Return: 0 if read successfully otherwise negative error code
+ */
+static int get_message(struct mbox_context *context, union mbox_regs *msg)
+{
+ int rc;
+
+ rc = read(context->fds[MBOX_FD].fd, msg, sizeof(msg->raw));
+ if (rc < 0) {
+ MSG_ERR("Couldn't read: %s\n", strerror(errno));
+ return -errno;
+ } else if (rc < sizeof(msg->raw)) {
+ MSG_ERR("Short read: %d expecting %zu\n", rc, sizeof(msg->raw));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * dispatch_mbox() - handle an mbox interrupt
+ * @context: The mbox context pointer
+ *
+ * Return: 0 if handled successfully otherwise negative error code
+ */
+int dispatch_mbox(struct mbox_context *context)
+{
+ int rc = 0;
+ union mbox_regs req = { 0 };
+
+ assert(context);
+
+ MSG_OUT("Dispatched to mbox\n");
+ rc = get_message(context, &req);
+ if (rc) {
+ return rc;
+ }
+
+ return handle_mbox_req(context, &req);
+}
+
+int init_mbox_dev(struct mbox_context *context)
+{
+ int fd;
+
+ /* Open MBOX Device */
+ fd = open(MBOX_HOST_PATH, O_RDWR | O_NONBLOCK);
+ if (fd < 0) {
+ MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n",
+ MBOX_HOST_PATH, strerror(errno));
+ return -errno;
+ }
+
+ context->fds[MBOX_FD].fd = fd;
+
+ return 0;
+}
+
+void free_mbox_dev(struct mbox_context *context)
+{
+ close(context->fds[MBOX_FD].fd);
+}
diff --git a/mboxd_msg.h b/mboxd_msg.h
new file mode 100644
index 0000000..44a2f91
--- /dev/null
+++ b/mboxd_msg.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 IBM
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef MBOXD_MSG_H
+#define MBOXD_MSG_H
+
+#define NO_BMC_EVENT false
+#define SET_BMC_EVENT true
+
+struct mbox_msg {
+ uint8_t command;
+ uint8_t seq;
+ uint8_t args[MBOX_ARGS_BYTES];
+ uint8_t response;
+};
+
+union mbox_regs {
+ char raw[MBOX_REG_BYTES];
+ struct mbox_msg msg;
+};
+
+int set_bmc_events(struct mbox_context *context, uint8_t bmc_event,
+ bool write_back);
+int clr_bmc_events(struct mbox_context *context, uint8_t bmc_event,
+ bool write_back);
+int dispatch_mbox(struct mbox_context *context);
+int init_mbox_dev(struct mbox_context *context);
+void free_mbox_dev(struct mbox_context *context);
+
+#endif /* MBOXD_MSG_H */
diff --git a/mboxd_windows.c b/mboxd_windows.c
new file mode 100644
index 0000000..fd46770
--- /dev/null
+++ b/mboxd_windows.c
@@ -0,0 +1,588 @@
+/*
+ * Mailbox Daemon Window Helpers
+ *
+ * Copyright 2016 IBM
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/timerfd.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <mtd/mtd-abi.h>
+
+#include "mbox.h"
+#include "common.h"
+#include "mboxd_msg.h"
+#include "mboxd_windows.h"
+#include "mboxd_flash.h"
+
+/* Initialisation Functions */
+
+/*
+ * init_window_state() - Initialise a new window to a known state
+ * @window: The window to initialise
+ * @size: The size of the window
+ */
+void init_window_state(struct window_context *window, uint32_t size)
+{
+ window->mem = NULL;
+ window->flash_offset = FLASH_OFFSET_UNINIT;
+ window->size = size;
+ window->dirty_bmap = NULL;
+ window->age = 0;
+}
+
+/*
+ * init_window_mem() - Divide the reserved memory region among the windows
+ * @context: The mbox context pointer
+ *
+ * Return: 0 on success otherwise negative error code
+ */
+int init_window_mem(struct mbox_context *context)
+{
+ void *mem_location = context->mem;
+ int i;
+
+ /*
+ * Carve up the reserved memory region and allocate it to each of the
+ * windows. The windows are placed one after the other in ascending
+ * order, so the first window will be first in memory and so on. We
+ * shouldn't have allocated more windows than we have memory, but if we
+ * did we will error out here
+ */
+ for (i = 0; i < context->windows.num; i++) {
+ context->windows.window[i].mem = mem_location;
+ mem_location += context->windows.window[i].size;
+ if (mem_location > (context->mem + context->mem_size)) {
+ /* Tried to allocate window past the end of memory */
+ MSG_ERR("Total size of windows exceeds reserved mem\n");
+ MSG_ERR("Try smaller or fewer windows\n");
+ MSG_ERR("Mem size: 0x%.8x\n", context->mem_size);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* Write from Window Functions */
+
+/*
+ * write_from_window_v1() - Handle writing when erase and block size differ
+ * @context: The mbox context pointer
+ * @offset_bytes: The offset in the current window to write from (bytes)
+ * @count_bytes: Number of bytes to write
+ *
+ * Handle a write_from_window for dirty memory when block_size is less than the
+ * flash erase size
+ * This requires us to be a bit careful because we might have to erase more
+ * than we want to write which could result in data loss if we don't have the
+ * entire portion of flash to be erased already saved in memory (for us to
+ * write back after the erase)
+ *
+ * Return: 0 on success otherwise negative error code
+ */
+int write_from_window_v1(struct mbox_context *context,
+ uint32_t offset_bytes, uint32_t count_bytes)
+{
+ int rc;
+ uint32_t flash_offset;
+ struct window_context low_mem = { 0 }, high_mem = { 0 };
+
+ /* Find where in phys flash this is based on the window.flash_offset */
+ flash_offset = context->current->flash_offset + offset_bytes;
+
+ /*
+ * low_mem.flash_offset = erase boundary below where we're writing
+ * low_mem.size = size from low_mem.flash_offset to where we're writing
+ *
+ * high_mem.flash_offset = end of where we're writing
+ * high_mem.size = size from end of where we're writing to next erase
+ * boundary
+ */
+ low_mem.flash_offset = align_down(flash_offset,
+ context->mtd_info.erasesize);
+ low_mem.size = flash_offset - low_mem.flash_offset;
+ high_mem.flash_offset = flash_offset + count_bytes;
+ high_mem.size = align_up(high_mem.flash_offset,
+ context->mtd_info.erasesize) -
+ high_mem.flash_offset;
+
+ /*
+ * Check if we already have a copy of the required flash areas in
+ * memory as part of the existing window
+ */
+ if (low_mem.flash_offset < context->current->flash_offset) {
+ /* Before the start of our current window */
+ low_mem.mem = malloc(low_mem.size);
+ if (!low_mem.mem) {
+ MSG_ERR("Unable to allocate memory\n");
+ return -MBOX_R_SYSTEM_ERROR;
+ }
+ rc = copy_flash(context, low_mem.flash_offset,
+ low_mem.mem, low_mem.size);
+ if (rc < 0) {
+ goto out;
+ }
+ }
+ if ((high_mem.flash_offset + high_mem.size) >
+ (context->current->flash_offset + context->current->size)) {
+ /* After the end of our current window */
+ high_mem.mem = malloc(high_mem.size);
+ if (!high_mem.mem) {
+ MSG_ERR("Unable to allocate memory\n");
+ rc = -MBOX_R_SYSTEM_ERROR;
+ goto out;
+ }
+ rc = copy_flash(context, high_mem.flash_offset,
+ high_mem.mem, high_mem.size);
+ if (rc < 0) {
+ goto out;
+ }
+ }
+
+ /*
+ * We need to erase the flash from low_mem.flash_offset->
+ * high_mem.flash_offset + high_mem.size
+ */
+ rc = erase_flash(context, low_mem.flash_offset,
+ (high_mem.flash_offset - low_mem.flash_offset) +
+ high_mem.size);
+ if (rc < 0) {
+ MSG_ERR("Couldn't erase flash\n");
+ goto out;
+ }
+
+ /* Write back over the erased area */
+ if (low_mem.mem) {
+ /* Exceed window at the start */
+ rc = write_flash(context, low_mem.flash_offset, low_mem.mem,
+ low_mem.size);
+ if (rc < 0) {
+ goto out;
+ }
+ }
+ rc = write_flash(context, flash_offset,
+ context->current->mem + offset_bytes, count_bytes);
+ if (rc < 0) {
+ goto out;
+ }
+ /*
+ * We still need to write the last little bit that we erased - it's
+ * either in the current window or the high_mem window.
+ */
+ if (high_mem.mem) {
+ /* Exceed window at the end */
+ rc = write_flash(context, high_mem.flash_offset, high_mem.mem,
+ high_mem.size);
+ if (rc < 0) {
+ goto out;
+ }
+ } else {
+ /* Write from the current window - it's atleast that big */
+ rc = write_flash(context, high_mem.flash_offset,
+ context->current->mem + offset_bytes +
+ count_bytes, high_mem.size);
+ if (rc < 0) {
+ goto out;
+ }
+ }
+
+out:
+ free(low_mem.mem);
+ free(high_mem.mem);
+ return rc;
+}
+
+/*
+ * write_from_window() - Write back to the flash from the current window
+ * @context: The mbox context pointer
+ * @offset_bytes: The offset in the current window to write from (blocks)
+ * @count_bytes: Number of blocks to write
+ * @type: Whether this is an erase & write or just an erase
+ *
+ * Return: 0 on success otherwise negative error code
+ */
+int write_from_window(struct mbox_context *context, uint32_t offset,
+ uint32_t count, uint8_t type)
+{
+ int rc;
+ uint32_t flash_offset, count_bytes = count << context->block_size_shift;
+ uint32_t offset_bytes = offset << context->block_size_shift;
+
+ switch (type) {
+ case WINDOW_ERASED: /* >= V2 ONLY -> block_size == erasesize */
+ flash_offset = context->current->flash_offset + offset_bytes;
+ rc = erase_flash(context, flash_offset, count_bytes);
+ if (rc < 0) {
+ MSG_ERR("Couldn't erase flash\n");
+ return rc;
+ }
+ break;
+ case WINDOW_DIRTY:
+ /*
+ * For protocol V1, block_size may be smaller than erase size
+ * so we have a special function to make sure that we do this
+ * correctly without losing data.
+ */
+ if (log_2(context->mtd_info.erasesize) !=
+ context->block_size_shift) {
+ return write_from_window_v1(context, offset_bytes,
+ count_bytes);
+ }
+ flash_offset = context->current->flash_offset + offset_bytes;
+
+ /* Erase the flash */
+ rc = erase_flash(context, flash_offset, count_bytes);
+ if (rc < 0) {
+ return rc;
+ }
+
+ /* Write to the erased flash */
+ rc = write_flash(context, flash_offset,
+ context->current->mem + offset_bytes,
+ count_bytes);
+ if (rc < 0) {
+ return rc;
+ }
+
+ break;
+ default:
+ /* We shouldn't be able to get here */
+ MSG_ERR("Write from window with invalid type: %d\n", type);
+ return -MBOX_R_SYSTEM_ERROR;
+ }
+
+ return 0;
+}
+
+/* Window Management Functions */
+
+/*
+ * alloc_window_dirty_bytemap() - (re)allocate all the window dirty bytemaps
+ * @context: The mbox context pointer
+ */
+void alloc_window_dirty_bytemap(struct mbox_context *context)
+{
+ struct window_context *cur;
+ int i;
+
+ for (i = 0; i < context->windows.num; i++) {
+ cur = &context->windows.window[i];
+ /* There may already be one allocated */
+ free(cur->dirty_bmap);
+ /* Allocate the new one */
+ cur->dirty_bmap = calloc((cur->size >>
+ context->block_size_shift),
+ sizeof(*cur->dirty_bmap));
+ }
+}
+
+/*
+ * free_window_dirty_bytemap() - free all window dirty bytemaps
+ * @context: The mbox context pointer
+ */
+void free_window_dirty_bytemap(struct mbox_context *context)
+{
+ int i;
+
+ for (i = 0; i < context->windows.num; i++) {
+ free(context->windows.window[i].dirty_bmap);
+ }
+}
+
+/*
+ * set_window_bytemap() - Set the window bytemap
+ * @context: The mbox context pointer
+ * @cur: The window to set the bytemap of
+ * @offset: Where in the window to set the bytemap (blocks)
+ * @size: The number of blocks to set
+ * @val: The value to set the bytemap to
+ *
+ * Return: 0 on success otherwise negative error code
+ */
+int set_window_bytemap(struct mbox_context *context, struct window_context *cur,
+ uint32_t offset, uint32_t size, uint8_t val)
+{
+ if (offset + size > (cur->size >> context->block_size_shift)) {
+ MSG_ERR("Tried to set window bytemap past end of window\n");
+ MSG_ERR("Requested offset: 0x%x size: 0x%x window size: 0x%x\n",
+ offset << context->block_size_shift,
+ size << context->block_size_shift,
+ cur->size << context->block_size_shift);
+ return -MBOX_R_PARAM_ERROR;
+ }
+
+ memset(cur->dirty_bmap + offset, val, size);
+ return 0;
+}
+
+/*
+ * close_current_window() - Close the current (active) window
+ * @context: The mbox context pointer
+ * @set_bmc_event: Whether to set the bmc event bit
+ * @flags: Flags as defined for a close command in the protocol
+ *
+ * This closes the current window. If the host has requested the current window
+ * be closed then we don't need to set the bmc event bit
+ * (set_bmc_event == false), otherwise if the current window has been closed
+ * without the host requesting it the bmc event bit must be set to indicate this
+ * to the host (set_bmc_event == true).
+ */
+void close_current_window(struct mbox_context *context, bool set_bmc_event,
+ uint8_t flags)
+{
+ if (set_bmc_event) {
+ set_bmc_events(context, BMC_EVENT_WINDOW_RESET, SET_BMC_EVENT);
+ }
+
+ if (flags & FLAGS_SHORT_LIFETIME) {
+ context->current->age = 0;
+ }
+
+ context->current->size = context->windows.default_size;
+ context->current = NULL;
+ context->current_is_write = false;
+}
+
+/*
+ * reset_window() - Reset a window context to a well defined default state
+ * @context: The mbox context pointer
+ * @window: The window to reset
+ */
+void reset_window(struct mbox_context *context, struct window_context *window)
+{
+ window->flash_offset = FLASH_OFFSET_UNINIT;
+ window->size = context->windows.default_size;
+ if (window->dirty_bmap) { /* Might not have been allocated */
+ set_window_bytemap(context, window, 0,
+ window->size >> context->block_size_shift,
+ WINDOW_CLEAN);
+ }
+ window->age = 0;
+}
+
+/*
+ * reset_all_windows() - Reset all windows to a well defined default state
+ * @context: The mbox context pointer
+ * @set_bmc_event: If any state change should be indicated to the host
+ */
+void reset_all_windows(struct mbox_context *context, bool set_bmc_event)
+{
+ int i;
+
+ /* We might have an open window which needs closing */
+ if (context->current) {
+ close_current_window(context, set_bmc_event, FLAGS_NONE);
+ }
+ for (i = 0; i < context->windows.num; i++) {
+ reset_window(context, &context->windows.window[i]);
+ }
+
+ context->windows.max_age = 0;
+}
+
+/*
+ * find_oldest_window() - Find the oldest (Least Recently Used) window
+ * @context: The mbox context pointer
+ *
+ * Return: Pointer to the least recently used window
+ */
+struct window_context *find_oldest_window(struct mbox_context *context)
+{
+ struct window_context *oldest = NULL, *cur;
+ uint32_t min_age = context->windows.max_age + 1;
+ int i;
+
+ for (i = 0; i < context->windows.num; i++) {
+ cur = &context->windows.window[i];
+
+ if (cur->age < min_age) {
+ min_age = cur->age;
+ oldest = cur;
+ }
+ }
+
+ return oldest;
+}
+
+/*
+ * search_windows() - Search the window cache for a window containing offset
+ * @context: The mbox context pointer
+ * @offset: Absolute flash offset to search for (bytes)
+ * @exact: If the window must exactly map the requested offset
+ *
+ * This will search the cache of windows for one containing the requested
+ * offset. For V1 of the protocol windows must exactly map the offset since we
+ * can't tell the host how much of its request we actually mapped and it will
+ * thus assume it can access window->size from the offset we give it.
+ *
+ * Return: Pointer to a window containing the requested offset otherwise
+ * NULL
+ */
+struct window_context *search_windows(struct mbox_context *context,
+ uint32_t offset, bool exact)
+{
+ struct window_context *cur;
+ int i;
+
+ for (i = 0; i < context->windows.num; i++) {
+ cur = &context->windows.window[i];
+ if (cur->flash_offset == FLASH_OFFSET_UNINIT) {
+ /* Uninitialised Window */
+ if (offset == FLASH_OFFSET_UNINIT) {
+ return cur;
+ }
+ continue;
+ }
+ if ((offset >= cur->flash_offset) &&
+ (offset < (cur->flash_offset + cur->size))) {
+ if (exact && (cur->flash_offset != offset)) {
+ continue;
+ }
+ /* This window contains the requested offset */
+ cur->age = ++(context->windows.max_age);
+ return cur;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * create_map_window() - Create a window mapping which maps the requested offset
+ * @context: The mbox context pointer
+ * @this_window: A pointer to update to the "new" window
+ * @offset: Absolute flash offset to create a mapping for (bytes)
+ * @exact: If the window must exactly map the requested offset
+ *
+ * This is used to create a window mapping for the requested offset when there
+ * is no existing window in the cache which satisfies the offset. This involves
+ * choosing an existing window from the window cache to evict so we can use it
+ * to store the flash contents from the requested offset, we then point the
+ * caller to that window since it now maps their request.
+ *
+ * Return: 0 on success otherwise negative error code
+ */
+int create_map_window(struct mbox_context *context,
+ struct window_context **this_window, uint32_t offset,
+ bool exact)
+{
+ struct window_context *cur = NULL;
+ int rc;
+
+
+ /* Search for an uninitialised window, use this before evicting */
+ cur = search_windows(context, FLASH_OFFSET_UNINIT, true);
+
+ /* No uninitialised window found, we need to choose one to "evict" */
+ if (!cur) {
+ cur = find_oldest_window(context);
+ }
+
+ if (!exact) {
+ /*
+ * It would be nice to align the offsets which we map to window
+ * size, this will help prevent overlap which would be an
+ * inefficient use of our reserved memory area (we would like
+ * to "cache" as much of the acutal flash as possible in
+ * memory). If we're protocol V1 however we must ensure the
+ * offset requested is exactly mapped.
+ */
+ offset &= ~(cur->size - 1);
+ }
+
+ if ((offset + cur->size) > context->flash_size) {
+ /*
+ * There is V1 skiboot implementations out there which don't
+ * mask offset with window size, meaning when we have
+ * window size == flash size we will never allow the host to
+ * open a window except at 0x0, which isn't always where the
+ * host requests it. Thus we have to ignore this check and just
+ * hope the host doesn't access past the end of the window
+ * (which it shouldn't) for V1 implementations to get around
+ * this.
+ */
+ if (context->version == API_VERSION_1) {
+ cur->size = align_down(context->flash_size - offset,
+ 1 << context->block_size_shift);
+ } else {
+ /* Trying to read past the end of flash */
+ MSG_ERR("Tried to open read window past flash limit\n");
+ return -MBOX_R_PARAM_ERROR;
+ }
+ }
+
+ /* Copy from flash into the window buffer */
+ rc = copy_flash(context, offset, cur->mem, cur->size);
+ if (rc < 0) {
+ /* We don't know how much we've copied -> better reset window */
+ reset_window(context, cur);
+ return rc;
+ }
+
+ /*
+ * Since for V1 windows aren't constrained to start at multiples of
+ * window size it's possible that something already maps this offset.
+ * Reset any windows which map this offset to avoid coherency problems.
+ * We just have to check for anything which maps the start or the end
+ * of the window since all windows are the same size so another window
+ * cannot map just the middle of this window.
+ */
+ if (context->version == API_VERSION_1) {
+ uint32_t i;
+
+ for (i = offset; i < (offset + cur->size); i += (cur->size - 1)) {
+ struct window_context *tmp = NULL;
+ do {
+ tmp = search_windows(context, i, false);
+ if (tmp) {
+ reset_window(context, tmp);
+ }
+ } while (tmp);
+ }
+ }
+
+ /* Clear the bytemap of the window just loaded -> we know it's clean */
+ set_window_bytemap(context, cur, 0,
+ cur->size >> context->block_size_shift,
+ WINDOW_CLEAN);
+
+ /* Update so we know what's in the window */
+ cur->flash_offset = offset;
+ cur->age = ++(context->windows.max_age);
+ *this_window = cur;
+
+ return 0;
+}
diff --git a/mboxd_windows.h b/mboxd_windows.h
new file mode 100644
index 0000000..90e1d46
--- /dev/null
+++ b/mboxd_windows.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 IBM
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef MBOXD_WINDOWS_H
+#define MBOXD_WINDOWS_H
+
+#define NO_FLUSH false
+#define WITH_FLUSH true
+
+/* Initialisation Functions */
+void init_window_state(struct window_context *window, uint32_t size);
+int init_window_mem(struct mbox_context *context);
+/* Write From Window Functions */
+int write_from_window_v1(struct mbox_context *context,
+ uint32_t offset_bytes, uint32_t count_bytes);
+int write_from_window(struct mbox_context *context, uint32_t offset,
+ uint32_t count, uint8_t type);
+/* Window Management Functions */
+void alloc_window_dirty_bytemap(struct mbox_context *context);
+void free_window_dirty_bytemap(struct mbox_context *context);
+int set_window_bytemap(struct mbox_context *context, struct window_context *cur,
+ uint32_t offset, uint32_t size, uint8_t val);
+void close_current_window(struct mbox_context *context, bool set_bmc_event,
+ uint8_t flags);
+void reset_window(struct mbox_context *context, struct window_context *window);
+void reset_all_windows(struct mbox_context *context, bool set_bmc_event);
+struct window_context *find_oldest_window(struct mbox_context *context);
+struct window_context *search_windows(struct mbox_context *context,
+ uint32_t offset, bool exact);
+int create_map_window(struct mbox_context *context,
+ struct window_context **this_window,
+ uint32_t offset, bool exact);
+
+#endif /* MBOXD_WINDOWS_H */
OpenPOWER on IntegriCloud