summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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