summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-04-19 16:10:11 -0700
committerRandall Spangler <rspangler@chromium.org>2012-04-19 18:15:18 -0700
commit13ad1c007bef3922e0aae8c7e2ef067a05eb0c06 (patch)
treeccba49c2460301a85e0abe050a27c734fde1acdb
parent24dafefb3a63c9e2111ff87c4595ceaff7182d20 (diff)
downloadchrome-ec-13ad1c007bef3922e0aae8c7e2ef067a05eb0c06.tar.gz
Implement HOOK_SYSJUMP and use it to preserve LPC host event mask
This also changes shared_mem to use all the remaining RAM, instead of reserving a fixed-size buffer. Signed-off-by: Randall Spangler <rspangler@chromium.org> BUG=chrome-os-partner:9161 TEST=manual hostevent --> all masks should be 0 hostevent smi 0x12300000 hostevent --> should confirm SMI mask was set sysjump b hostevent --> should confirm SMI mask is still set reboot hostevent --> should confirm SMI mask is back to 0 Change-Id: Iccb6da6ccc93ee5036a3f478d24b717a462d9150
-rw-r--r--chip/lm4/lpc.c230
-rw-r--r--common/hooks.c31
-rw-r--r--common/shared_mem.c27
-rw-r--r--common/system_common.c113
-rw-r--r--core/cortex-m/ec.lds.S9
-rw-r--r--core/cortex-m/link_defs.h4
-rw-r--r--include/hooks.h8
-rw-r--r--include/system.h17
8 files changed, 297 insertions, 142 deletions
diff --git a/chip/lm4/lpc.c b/chip/lm4/lpc.c
index c8b45df798..a5dfedefc8 100644
--- a/chip/lm4/lpc.c
+++ b/chip/lm4/lpc.c
@@ -14,10 +14,13 @@
#include "lpc_commands.h"
#include "port80.h"
#include "registers.h"
+#include "system.h"
#include "task.h"
#include "timer.h"
#include "uart.h"
+#include "util.h"
+#define LPC_SYSJUMP_TAG 0x4c50 /* "LP" */
static uint32_t host_events; /* Currently pending SCI/SMI events */
static uint32_t event_mask[3]; /* Event masks for each type */
@@ -106,104 +109,6 @@ static void lpc_generate_sci(void)
}
-static int lpc_init(void)
-{
- volatile uint32_t scratch __attribute__((unused));
-
- /* Enable RGCGLPC then delay a few clocks. */
- LM4_SYSTEM_RCGCLPC = 1;
- scratch = LM4_SYSTEM_RCGCLPC;
-
- LM4_LPC_LPCIM = 0;
- LM4_LPC_LPCCTL = 0;
- LM4_LPC_LPCIRQCTL = 0;
-
- /* Configure GPIOs */
- configure_gpio();
-
- /* Set LPC channel 0 to I/O address 0x62 (data) / 0x66 (command),
- * single endpoint, offset 0 for host command/writes and 1 for EC
- * data writes, pool bytes 0(data)/1(cmd) */
- LM4_LPC_ADR(LPC_CH_KERNEL) = EC_LPC_ADDR_KERNEL_DATA;
- LM4_LPC_CTL(LPC_CH_KERNEL) = (LPC_POOL_OFFS_KERNEL << (5 - 1));
- /* Unmask interrupt for host command writes */
- LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_KERNEL, 4);
-
- /* Set LPC channel 1 to I/O address 0x80 (data), single endpoint,
- * pool bytes 4(data)/5(cmd). */
- LM4_LPC_ADR(LPC_CH_PORT80) = 0x80;
- LM4_LPC_CTL(LPC_CH_PORT80) = (LPC_POOL_OFFS_PORT80 << (5 - 1));
- /* Unmask interrupt for host data writes */
- LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_PORT80, 2);
-
-
- /* Set LPC channel 2 to I/O address 0x800, range endpoint,
- * arbitration disabled, pool bytes 512-1023. To access this from
- * x86, use the following commands to set GEN_LPC2 and GEN_LPC3:
- *
- * pci_write32 0 0x1f 0 0x88 0x007c0801
- * pci_write32 0 0x1f 0 0x8c 0x007c0901
- */
- LM4_LPC_ADR(LPC_CH_CMD_DATA) = EC_LPC_ADDR_KERNEL_PARAM;
- LM4_LPC_CTL(LPC_CH_CMD_DATA) = 0x801D |
- (LPC_POOL_OFFS_CMD_DATA << (5 - 1));
-
- /* Set LPC channel 3 to I/O address 0x60 (data) / 0x64 (command),
- * single endpoint, offset 0 for host command/writes and 1 for EC
- * data writes, pool bytes 0(data)/1(cmd) */
- LM4_LPC_ADR(LPC_CH_KEYBOARD) = 0x60;
- LM4_LPC_CTL(LPC_CH_KEYBOARD) = (1 << 24/* IRQSEL1 */) |
- (0 << 18/* IRQEN1 */) | (LPC_POOL_OFFS_KEYBOARD << (5 - 1));
- LM4_LPC_ST(LPC_CH_KEYBOARD) = 0;
- /* Unmask interrupt for host command/data writes and data reads */
- LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_KEYBOARD, 7);
-
- /* Set LPC channel 4 to I/O address 0x200 (data) / 0x204 (command),
- * single endpoint, offset 0 for host command/writes and 1 for EC
- * data writes, pool bytes 0(data)/1(cmd) */
- LM4_LPC_ADR(LPC_CH_USER) = EC_LPC_ADDR_USER_DATA;
- LM4_LPC_CTL(LPC_CH_USER) = (LPC_POOL_OFFS_USER << (5 - 1));
- /* Unmask interrupt for host command writes */
- LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_USER, 4);
-
- /* Set LPC channel 7 to COM port I/O address. Note that channel 7
- * ignores the TYPE bit and is always an 8-byte range. */
- LM4_LPC_ADR(LPC_CH_COMX) = LPC_COMX_ADDR;
- /* TODO: could configure IRQSELs and set IRQEN2/CX, and then the host
- * can enable IRQs on its own. */
- LM4_LPC_CTL(LPC_CH_COMX) = 0x0004 | (LPC_POOL_OFFS_COMX << (5 - 1));
- /* Enable COMx emulation for reads and writes. */
- LM4_LPC_LPCDMACX = 0x00310000;
- /* Unmask interrupt for host data writes. We don't need interrupts for
- * reads, because there's no flow control in that direction; LPC is
- * much faster than the UART, and the UART doesn't have anywhere
- * sensible to buffer input anyway. */
- LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_COMX, 2);
-
- /* Unmaksk LPC bus reset interrupt. This lets us monitor the PCH
- * PLTRST# signal for debugging. */
- LM4_LPC_LPCIM |= (1 << 31);
-
- /* Enable LPC channels */
- LM4_LPC_LPCCTL = LM4_LPC_SCI_CLK_1 |
- (1 << LPC_CH_KERNEL) |
- (1 << LPC_CH_PORT80) |
- (1 << LPC_CH_CMD_DATA) |
- (1 << LPC_CH_KEYBOARD) |
- (1 << LPC_CH_USER) |
- (1 << LPC_CH_COMX);
-
- /* Enable LPC interrupt */
- task_enable_irq(LM4_IRQ_LPC);
-
- /* Enable COMx UART */
- uart_comx_enable();
-
- return EC_SUCCESS;
-}
-DECLARE_HOOK(HOOK_INIT, lpc_init, HOOK_PRIO_DEFAULT);
-
-
uint8_t *lpc_get_host_range(int slot)
{
return (uint8_t *)LPC_POOL_CMD_DATA + EC_LPC_PARAM_SIZE * slot;
@@ -438,9 +343,136 @@ static void lpc_interrupt(void)
/* Debugging: print changes to LPC0RESET */
if (mis & (1 << 31)) {
- uart_printf("[%T LPC PLTRST# %sasserted]\n",
+ uart_printf("[%T LPC RESET# %sasserted]\n",
(LM4_LPC_LPCSTS & (1<<10)) ? "" : "de");
}
}
DECLARE_IRQ(LM4_IRQ_LPC, lpc_interrupt, 2);
+
+/* Preserve event masks across a sysjump */
+static int lpc_sysjump(void)
+{
+ system_add_jump_tag(LPC_SYSJUMP_TAG, 1,
+ sizeof(event_mask), event_mask);
+
+ return EC_SUCCESS;
+}
+DECLARE_HOOK(HOOK_SYSJUMP, lpc_sysjump, HOOK_PRIO_DEFAULT);
+
+
+/* Restore event masks after a sysjump */
+static void lpc_post_sysjump(void)
+{
+ const uint32_t *prev_mask;
+ int size, version;
+
+ prev_mask = (const uint32_t *)system_get_jump_tag(LPC_SYSJUMP_TAG,
+ &version, &size);
+ if (!prev_mask || version != 1 || size != sizeof(event_mask))
+ return;
+
+ memcpy(event_mask, prev_mask, sizeof(event_mask));
+ update_host_event_status();
+}
+
+
+static int lpc_init(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable RGCGLPC then delay a few clocks. */
+ LM4_SYSTEM_RCGCLPC = 1;
+ scratch = LM4_SYSTEM_RCGCLPC;
+
+ LM4_LPC_LPCIM = 0;
+ LM4_LPC_LPCCTL = 0;
+ LM4_LPC_LPCIRQCTL = 0;
+
+ /* Configure GPIOs */
+ configure_gpio();
+
+ /* Set LPC channel 0 to I/O address 0x62 (data) / 0x66 (command),
+ * single endpoint, offset 0 for host command/writes and 1 for EC
+ * data writes, pool bytes 0(data)/1(cmd) */
+ LM4_LPC_ADR(LPC_CH_KERNEL) = EC_LPC_ADDR_KERNEL_DATA;
+ LM4_LPC_CTL(LPC_CH_KERNEL) = (LPC_POOL_OFFS_KERNEL << (5 - 1));
+ /* Unmask interrupt for host command writes */
+ LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_KERNEL, 4);
+
+ /* Set LPC channel 1 to I/O address 0x80 (data), single endpoint,
+ * pool bytes 4(data)/5(cmd). */
+ LM4_LPC_ADR(LPC_CH_PORT80) = 0x80;
+ LM4_LPC_CTL(LPC_CH_PORT80) = (LPC_POOL_OFFS_PORT80 << (5 - 1));
+ /* Unmask interrupt for host data writes */
+ LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_PORT80, 2);
+
+
+ /* Set LPC channel 2 to I/O address 0x800, range endpoint,
+ * arbitration disabled, pool bytes 512-1023. To access this from
+ * x86, use the following commands to set GEN_LPC2 and GEN_LPC3:
+ *
+ * pci_write32 0 0x1f 0 0x88 0x007c0801
+ * pci_write32 0 0x1f 0 0x8c 0x007c0901
+ */
+ LM4_LPC_ADR(LPC_CH_CMD_DATA) = EC_LPC_ADDR_KERNEL_PARAM;
+ LM4_LPC_CTL(LPC_CH_CMD_DATA) = 0x801D |
+ (LPC_POOL_OFFS_CMD_DATA << (5 - 1));
+
+ /* Set LPC channel 3 to I/O address 0x60 (data) / 0x64 (command),
+ * single endpoint, offset 0 for host command/writes and 1 for EC
+ * data writes, pool bytes 0(data)/1(cmd) */
+ LM4_LPC_ADR(LPC_CH_KEYBOARD) = 0x60;
+ LM4_LPC_CTL(LPC_CH_KEYBOARD) = (1 << 24/* IRQSEL1 */) |
+ (0 << 18/* IRQEN1 */) | (LPC_POOL_OFFS_KEYBOARD << (5 - 1));
+ LM4_LPC_ST(LPC_CH_KEYBOARD) = 0;
+ /* Unmask interrupt for host command/data writes and data reads */
+ LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_KEYBOARD, 7);
+
+ /* Set LPC channel 4 to I/O address 0x200 (data) / 0x204 (command),
+ * single endpoint, offset 0 for host command/writes and 1 for EC
+ * data writes, pool bytes 0(data)/1(cmd) */
+ LM4_LPC_ADR(LPC_CH_USER) = EC_LPC_ADDR_USER_DATA;
+ LM4_LPC_CTL(LPC_CH_USER) = (LPC_POOL_OFFS_USER << (5 - 1));
+ /* Unmask interrupt for host command writes */
+ LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_USER, 4);
+
+ /* Set LPC channel 7 to COM port I/O address. Note that channel 7
+ * ignores the TYPE bit and is always an 8-byte range. */
+ LM4_LPC_ADR(LPC_CH_COMX) = LPC_COMX_ADDR;
+ /* TODO: could configure IRQSELs and set IRQEN2/CX, and then the host
+ * can enable IRQs on its own. */
+ LM4_LPC_CTL(LPC_CH_COMX) = 0x0004 | (LPC_POOL_OFFS_COMX << (5 - 1));
+ /* Enable COMx emulation for reads and writes. */
+ LM4_LPC_LPCDMACX = 0x00310000;
+ /* Unmask interrupt for host data writes. We don't need interrupts for
+ * reads, because there's no flow control in that direction; LPC is
+ * much faster than the UART, and the UART doesn't have anywhere
+ * sensible to buffer input anyway. */
+ LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_COMX, 2);
+
+ /* Unmaksk LPC bus reset interrupt. This lets us monitor the PCH
+ * PLTRST# signal for debugging. */
+ LM4_LPC_LPCIM |= (1 << 31);
+
+ /* Enable LPC channels */
+ LM4_LPC_LPCCTL = LM4_LPC_SCI_CLK_1 |
+ (1 << LPC_CH_KERNEL) |
+ (1 << LPC_CH_PORT80) |
+ (1 << LPC_CH_CMD_DATA) |
+ (1 << LPC_CH_KEYBOARD) |
+ (1 << LPC_CH_USER) |
+ (1 << LPC_CH_COMX);
+
+ /* Enable LPC interrupt */
+ task_enable_irq(LM4_IRQ_LPC);
+
+ /* Enable COMx UART */
+ uart_comx_enable();
+
+ /* Restore event masks if needed */
+ lpc_post_sysjump();
+
+ return EC_SUCCESS;
+}
+DECLARE_HOOK(HOOK_INIT, lpc_init, HOOK_PRIO_DEFAULT);
diff --git a/common/hooks.c b/common/hooks.c
index 81bc2ac212..e8a64ebeb7 100644
--- a/common/hooks.c
+++ b/common/hooks.c
@@ -10,6 +10,20 @@
#include "uart.h"
#include "util.h"
+struct hook_ptrs {
+ const struct hook_data *start;
+ const struct hook_data *end;
+};
+
+/* Hook data start and end pointers for each type of hook. Must be in same
+ * order as enum hook_type. */
+static const struct hook_ptrs hook_list[] = {
+ {__hooks_init, __hooks_init_end},
+ {__hooks_freq_change, __hooks_freq_change_end},
+ {__hooks_sysjump, __hooks_sysjump_end},
+};
+
+
int hook_notify(enum hook_type type, int stop_on_error)
{
const struct hook_data *start, *end, *p;
@@ -17,21 +31,8 @@ int hook_notify(enum hook_type type, int stop_on_error)
int last_prio = HOOK_PRIO_FIRST - 1, prio;
int rv_error = EC_SUCCESS, rv;
- /* Get the start and end pointers for the hook type */
- switch (type) {
- case HOOK_INIT:
- start = __hooks_init;
- end = __hooks_init_end;
- break;
- case HOOK_FREQ_CHANGE:
- start = __hooks_freq_change;
- end = __hooks_freq_change_end;
- break;
- default:
- /* Unhandled hook type */
- return EC_ERROR_UNKNOWN;
- }
-
+ start = hook_list[type].start;
+ end = hook_list[type].end;
count = ((uint32_t)end - (uint32_t)start) / sizeof(struct hook_data);
/* Call all the hooks in priority order */
diff --git a/common/shared_mem.c b/common/shared_mem.c
index bff3ec6da3..63edd7e755 100644
--- a/common/shared_mem.c
+++ b/common/shared_mem.c
@@ -1,35 +1,30 @@
-/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* Shared memory module for Chrome EC */
+#include "config.h"
+#include "link_defs.h"
#include "shared_mem.h"
-#include "uart.h"
+#include "system.h"
-/* Size of shared memory buffer */
-#define SHARED_MEM_SIZE 4096
-
-static char shared_buf[SHARED_MEM_SIZE];
-static int buf_in_use = 0;
-
-
-int shared_mem_init(void)
-{
- return EC_SUCCESS;
-}
+static int buf_in_use;
int shared_mem_size(void)
{
- return SHARED_MEM_SIZE;
+ /* Use all the RAM we can. The shared memory buffer is the
+ * last thing allocated from the start of RAM, so we can use
+ * everything up to the jump data at the end of RAM. */
+ return system_usable_ram_end() - (uint32_t)__shared_mem_buf;
}
int shared_mem_acquire(int size, int wait, char **dest_ptr)
{
- if (size > SHARED_MEM_SIZE || size <= 0)
+ if (size > shared_mem_size() || size <= 0)
return EC_ERROR_INVAL;
/* TODO: if task_start() hasn't been called, fail immediately
@@ -42,7 +37,7 @@ int shared_mem_acquire(int size, int wait, char **dest_ptr)
/* TODO: atomically acquire buf_in_use. */
buf_in_use = 1;
- *dest_ptr = shared_buf;
+ *dest_ptr = __shared_mem_buf;
return EC_SUCCESS;
}
diff --git a/common/system_common.c b/common/system_common.c
index 7d6ed1e1e7..0d1805b2ca 100644
--- a/common/system_common.c
+++ b/common/system_common.c
@@ -6,6 +6,7 @@
/* System module for Chrome EC : common functions */
#include "console.h"
+#include "hooks.h"
#include "host_command.h"
#include "lpc.h"
#include "lpc_commands.h"
@@ -16,24 +17,55 @@
#include "version.h"
+struct jump_tag {
+ uint16_t tag;
+ uint8_t data_size;
+ uint8_t data_version;
+};
+
+
/* Data passed between the current image and the next one when jumping between
* images. */
#define JUMP_DATA_MAGIC 0x706d754a /* "Jump" */
-#define JUMP_DATA_VERSION 1
+#define JUMP_DATA_VERSION 2
struct jump_data {
/* Add new fields to the _start_ of the struct, since we copy it to the
* _end_ of RAM between images. This way, the magic number will always
* be the last word in RAM regardless of how many fields are added. */
- int reset_cause; /* Reset cause for the previous boot */
- int version; /* Version (JUMP_DATA_VERSION) */
- int magic; /* Magic number (JUMP_DATA_MAGIC) */
+
+ /* Fields from version 2 */
+ int jump_tag_total; /* Total size of all jump tags */
+
+ /* Fields from version 1 */
+ int reset_cause; /* Reset cause for the previous boot */
+ int version; /* Version (JUMP_DATA_VERSION) */
+ int magic; /* Magic number (JUMP_DATA_MAGIC). If this
+ * doesn't match at pre-init time, assume no valid
+ * data from the previous image. */
};
+/* Jump data goes at the end of RAM */
+static struct jump_data * const jdata =
+ (struct jump_data *)(CONFIG_RAM_BASE + CONFIG_RAM_SIZE
+ - sizeof(struct jump_data));
static enum system_reset_cause_t reset_cause = SYSTEM_RESET_UNKNOWN;
static int jumped_to_image;
+int system_usable_ram_end(void)
+{
+ /* Leave space at the end of RAM for jump data.
+ *
+ * Note that jump_tag_total is 0 on a reboot, so we have the maximum
+ * amount of RAM available on a reboot; we only lose space for stored
+ * tags after a sysjump. When verified boot runs after a reboot, it'll
+ * have as much RAM as we can give it; after verified boot jumps to
+ * another image there'll be less RAM, but we'll care less too. */
+ return (uint32_t)jdata - jdata->jump_tag_total;
+}
+
+
enum system_reset_cause_t system_get_reset_cause(void)
{
return reset_cause;
@@ -46,6 +78,56 @@ int system_jumped_to_this_image(void)
}
+int system_add_jump_tag(uint16_t tag, int version, int size, const void *data)
+{
+ struct jump_tag *t;
+
+ /* Only allowed during a sysjump */
+ if (jdata->magic != JUMP_DATA_MAGIC)
+ return EC_ERROR_UNKNOWN;
+
+ /* Make room for the new tag */
+ if (size > 255 || (size & 3))
+ return EC_ERROR_INVAL;
+ jdata->jump_tag_total += size + sizeof(struct jump_tag);
+
+ t = (struct jump_tag *)system_usable_ram_end();
+ t->tag = tag;
+ t->data_size = size;
+ t->data_version = version;
+ if (size)
+ memcpy(t + 1, data, size);
+
+ return EC_SUCCESS;
+}
+
+const uint8_t *system_get_jump_tag(uint16_t tag, int *version, int *size)
+{
+ const struct jump_tag *t;
+ int used = 0;
+
+ /* Search through tag data for a match */
+ while (used < jdata->jump_tag_total) {
+ /* Check the next tag */
+ t = (const struct jump_tag *)(system_usable_ram_end() + used);
+ used += sizeof(struct jump_tag) + t->data_size;
+ if (t->tag != tag)
+ continue;
+
+ /* Found a match */
+ if (size)
+ *size = t->data_size;
+ if (version)
+ *version = t->data_version;
+
+ return (const uint8_t *)(t + 1);
+ }
+
+ /* If we're still here, no match */
+ return NULL;
+}
+
+
void system_set_reset_cause(enum system_reset_cause_t cause)
{
reset_cause = cause;
@@ -112,8 +194,6 @@ const char *system_get_image_copy_string(void)
static void jump_to_image(uint32_t init_addr)
{
void (*resetvec)(void) = (void(*)(void))init_addr;
- struct jump_data *jdata = (struct jump_data *)
- (CONFIG_RAM_BASE + CONFIG_RAM_SIZE - sizeof(struct jump_data));
/* Flush UART output unless the UART hasn't been initialized yet */
if (uart_init_done())
@@ -126,6 +206,10 @@ static void jump_to_image(uint32_t init_addr)
jdata->magic = JUMP_DATA_MAGIC;
jdata->version = JUMP_DATA_VERSION;
jdata->reset_cause = reset_cause;
+ jdata->jump_tag_total = 0; /* Reset tags */
+
+ /* Call other hooks; these may add tags */
+ hook_notify(HOOK_SYSJUMP, 0);
/* Jump to the reset vector */
resetvec();
@@ -216,19 +300,26 @@ const char *system_get_build_info(void)
int system_common_pre_init(void)
{
- struct jump_data *jdata = (struct jump_data *)
- (CONFIG_RAM_BASE + CONFIG_RAM_SIZE - sizeof(struct jump_data));
-
/* Check jump data if this is a jump between images */
if (jdata->magic == JUMP_DATA_MAGIC &&
- jdata->version == JUMP_DATA_VERSION &&
+ jdata->version >= 1 &&
reset_cause == SYSTEM_RESET_SOFT_WARM) {
/* Yes, we jumped to this image */
jumped_to_image = 1;
/* Overwrite the reset cause with the real one */
reset_cause = jdata->reset_cause;
- /* Clear the jump struct's magic number */
+
+ /* Initialize fields added after version 1 */
+ if (jdata->version < 2)
+ jdata->jump_tag_total = 0;
+
+ /* Clear the jump struct's magic number. This prevents
+ * accidentally detecting a jump when there wasn't one, and
+ * disallows use of system_add_jump_tag(). */
jdata->magic = 0;
+ } else {
+ /* Clear the whole jump_data struct */
+ memset(jdata, 0, sizeof(struct jump_data));
}
return EC_SUCCESS;
diff --git a/core/cortex-m/ec.lds.S b/core/cortex-m/ec.lds.S
index 3b80f28093..32114b59da 100644
--- a/core/cortex-m/ec.lds.S
+++ b/core/cortex-m/ec.lds.S
@@ -50,10 +50,15 @@ SECTIONS
__hooks_init = .;
*(.rodata.HOOK_INIT)
__hooks_init_end = .;
+
__hooks_freq_change = .;
*(.rodata.HOOK_FREQ_CHANGE)
__hooks_freq_change_end = .;
+ __hooks_sysjump = .;
+ *(.rodata.HOOK_SYSJUMP)
+ __hooks_sysjump_end = .;
+
. = ALIGN(4);
*(.rodata*)
. = ALIGN(4);
@@ -79,6 +84,10 @@ SECTIONS
*(.bss)
. = ALIGN(4);
__bss_end = .;
+
+ /* Shared memory buffer must be at the end of preallocated RAM, so it
+ * can expand to use all the remaining RAM. */
+ __shared_mem_buf = .;
} > IRAM
/DISCARD/ : { *(.ARM.*) }
}
diff --git a/core/cortex-m/link_defs.h b/core/cortex-m/link_defs.h
index 9279f42804..af809ad7e0 100644
--- a/core/cortex-m/link_defs.h
+++ b/core/cortex-m/link_defs.h
@@ -20,6 +20,8 @@ extern const struct hook_data __hooks_init[];
extern const struct hook_data __hooks_init_end[];
extern const struct hook_data __hooks_freq_change[];
extern const struct hook_data __hooks_freq_change_end[];
+extern const struct hook_data __hooks_sysjump[];
+extern const struct hook_data __hooks_sysjump_end[];
extern const struct host_command __hcmds[];
extern const struct host_command __hcmds_end[];
@@ -27,4 +29,6 @@ extern const struct host_command __hcmds_end[];
extern const struct irq_priority __irqprio[];
extern const struct irq_priority __irqprio_end[];
+extern uint8_t __shared_mem_buf[];
+
#endif /* __CROS_EC_LINK_DEFS_H */
diff --git a/include/hooks.h b/include/hooks.h
index 889cf553bc..a0e12e708b 100644
--- a/include/hooks.h
+++ b/include/hooks.h
@@ -18,8 +18,14 @@ enum hook_priority {
enum hook_type {
- HOOK_INIT, /* System init */
+ HOOK_INIT = 0, /* System init */
HOOK_FREQ_CHANGE, /* System clock changed frequency */
+ HOOK_SYSJUMP, /* About to jump to another image. Modules which
+ * need to preserve data across such a jump should
+ * save it here and restore it in HOOK_INIT.
+ *
+ * NOTE: This hook is called with interrupts
+ * disabled! */
};
diff --git a/include/system.h b/include/system.h
index 36a8998b35..c84830bec4 100644
--- a/include/system.h
+++ b/include/system.h
@@ -70,6 +70,23 @@ enum system_image_copy_t system_get_image_copy(void);
* once since the last real boot. */
int system_jumped_to_this_image(void);
+/* Preserve data across a jump between images. <tag> identifies the data
+ * type. <size> must be a multiple of 4 bytes, and less than 255 bytes.
+ * <version> is the data version, so that tag data can evolve as firmware
+ * is updated. <data> points to the data to save.
+ *
+ * This may ONLY be called from within a HOOK_SYSJUMP handler. */
+int system_add_jump_tag(uint16_t tag, int version, int size, const void *data);
+
+/* Retrieve data stored by a previous image's call to
+ * system_add_jump_tag(). If a matching tag is found, retrieves
+ * <size> and <version>, and returns a pointer to the data. Returns
+ * NULL if no matching tag is found. */
+const uint8_t *system_get_jump_tag(uint16_t tag, int *version, int *size);
+
+/* Returns the address just past the last usable byte in RAM. */
+int system_usable_ram_end(void);
+
/* Returns true if the given range is overlapped with the active image. */
int system_unsafe_to_overwrite(uint32_t offset, uint32_t size);