summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2016-11-09 13:27:35 -0800
committerchrome-bot <chrome-bot@chromium.org>2016-11-11 23:11:51 -0800
commit12da6c23fbb9b72bc23d870d6283cf56ae7f448c (patch)
treeeff78fc73b85cf7dcca8371119eedaedad7a8a13
parent7300bc56c050d68485368d70d6dc123fbefcd6df (diff)
downloadchrome-ec-12da6c23fbb9b72bc23d870d6283cf56ae7f448c.tar.gz
Cr50: Add TPM-compliant commands for console lock
This allows custom TPM commands to be declared using the a DECLARE_VENDOR_COMMAND macro instead of the original (and still unchanged) DECLARE_EXTENSION_COMMAND macro. The new commands are nearly identical, but they are encapsulated using the vendor-specific protocols described in the TPMv2 spec. Our original extensions use a non-standard command code, and return a non-standard struct on completion, which can be confusing to standard TPM drivers and tools. Demonstrating the use of the new macros, this adds commands to obtain the state of the Cr50 restricted console lock, or to set the lock. There is intentionally no command to unlock the console. Note that this CL just adds the commands to the Cr50. We still need to provide a nice userspace utility for the AP to use. BUG=chrome-os-partner:58230 BUG=chrome-os-partner:57940 BRANCH=none TEST=make buildall; load, boot, test, and update again on Reef On Reef, I can use the trunks_send tool to send the raw TPM bytes to invoke these commands: Get the lock state: # trunks_send 80 01 00 00 00 0C 20 00 00 00 00 10 80010000000D00000000001000 The last byte 00 indicates that the lock is NOT set, so set it: # trunks_send 80 01 00 00 00 0C 20 00 00 00 00 10 80010000000C000000000011 Success. On the Cr50 console, I see it take effect: [480.080444 The console is locked] Query the state again: # trunks_send 80 01 00 00 00 0C 20 00 00 00 00 10 80010000000D00000000001001 and now the last byte 01 indicates that the console is locked. And of course the existing extension commands still work as before. In addition to uploading firmware, I can use the usb_updater from my build machine to query the running firmware version: $ ./extra/usb_updater/usb_updater -f open_device 18d1:5014 found interface 4 endpoint 5, chunk_len 64 READY ------- start Target running protocol version 5 Offsets: backup RO at 0x40000, backup RW at 0x4000 Keyids: RO 0x3716ee6b, RW 0xb93d6539 Current versions: RO 0.0.10 RW 0.0.9 $ Change-Id: I7fb1d888bf808c2ef0b2b07c782e926063cc2cc4 Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/409692 Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
-rw-r--r--board/cr50/wp.c75
-rw-r--r--common/extension.c16
-rw-r--r--common/tpm_registers.c31
-rw-r--r--include/extension.h123
4 files changed, 190 insertions, 55 deletions
diff --git a/board/cr50/wp.c b/board/cr50/wp.c
index 823abdf145..d667ecc74e 100644
--- a/board/cr50/wp.c
+++ b/board/cr50/wp.c
@@ -5,6 +5,7 @@
#include "common.h"
#include "console.h"
+#include "extension.h"
#include "gpio.h"
#include "hooks.h"
#include "nvmem.h"
@@ -49,6 +50,19 @@ static int console_restricted_state;
static int console_restricted_state = 1;
#endif
+static void lock_the_console(void)
+{
+ CPRINTS("The console is locked");
+ console_restricted_state = 1;
+}
+
+static void unlock_the_console(void)
+{
+ nvmem_wipe_or_reboot();
+ CPRINTS("TPM is erased, console is unlocked");
+ console_restricted_state = 0;
+}
+
int console_is_restricted(void)
{
return console_restricted_state;
@@ -90,9 +104,7 @@ static void unlock_sequence_is_over(void)
} else {
/* The last poke was after the final deadline, so we're done */
CPRINTS("Unlock process completed successfully");
- nvmem_wipe_or_reboot();
- console_restricted_state = 0;
- CPRINTS("TPM is erased, console is unlocked.");
+ unlock_the_console();
}
unlock_in_progress = 0;
@@ -146,6 +158,59 @@ static void start_unlock_process(int total_poking_time, int max_poke_interval)
}
/****************************************************************************/
+/* TPM vendor-specific commands */
+
+static enum vendor_cmd_rc vc_lock(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ uint8_t *buffer = buf;
+
+ if (code == VENDOR_CC_GET_LOCK) {
+ /*
+ * Get the state of the console lock.
+ *
+ * Args: none
+ * Returns: one byte; true (locked) or false (unlocked)
+ */
+ if (input_size != 0) {
+ *response_size = 0;
+ return VENDOR_RC_BOGUS_ARGS;
+ }
+
+ buffer[0] = console_is_restricted() ? 0x01 : 0x00;
+ *response_size = 1;
+ return VENDOR_RC_SUCCESS;
+ }
+
+ if (code == VENDOR_CC_SET_LOCK) {
+ /*
+ * Lock the console if it isn't already. Note that there
+ * intentionally isn't an unlock command. At most, we may want
+ * to call start_unlock_process(), but we haven't yet decided.
+ *
+ * Args: none
+ * Returns: none
+ */
+ if (input_size != 0) {
+ *response_size = 0;
+ return VENDOR_RC_BOGUS_ARGS;
+ }
+
+ lock_the_console();
+ *response_size = 0;
+ return VENDOR_RC_SUCCESS;
+ }
+
+ /* I have no idea what you're talking about */
+ *response_size = 0;
+ return VENDOR_RC_NO_SUCH_COMMAND;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_GET_LOCK, vc_lock);
+DECLARE_VENDOR_COMMAND(VENDOR_CC_SET_LOCK, vc_lock);
+
+/****************************************************************************/
static const char warning[] = "\n\t!!! WARNING !!!\n\n"
"\tThe AP will be impolitely shut down and the TPM persistent memory\n"
"\tERASED before the console is unlocked. If this is not what you\n"
@@ -161,12 +226,12 @@ static int command_lock(int argc, char **argv)
return EC_ERROR_PARAM1;
/* Changing nothing does nothing */
- if (enabled == console_restricted_state)
+ if (enabled == console_is_restricted())
goto out;
/* Locking the console is always allowed */
if (enabled) {
- console_restricted_state = 1;
+ lock_the_console();
goto out;
}
diff --git a/common/extension.c b/common/extension.c
index a588ce892e..d28bfad337 100644
--- a/common/extension.c
+++ b/common/extension.c
@@ -10,10 +10,10 @@
#define CPRINTF(format, args...) cprintf(CC_EXTENSION, format, ## args)
-void extension_route_command(uint16_t command_code,
- void *buffer,
- size_t in_size,
- size_t *out_size)
+uint32_t extension_route_command(uint16_t command_code,
+ void *buffer,
+ size_t in_size,
+ size_t *out_size)
{
struct extension_command *cmd_p;
struct extension_command *end_p;
@@ -22,10 +22,9 @@ void extension_route_command(uint16_t command_code,
end_p = (struct extension_command *)&__extension_cmds_end;
while (cmd_p != end_p) {
- if (cmd_p->command_code == command_code) {
- cmd_p->handler(buffer, in_size, out_size);
- return;
- }
+ if (cmd_p->command_code == command_code)
+ return cmd_p->handler(command_code, buffer,
+ in_size, out_size);
cmd_p++;
}
@@ -33,4 +32,5 @@ void extension_route_command(uint16_t command_code,
/* This covers the case of the handler not found. */
*out_size = 0;
+ return VENDOR_RC_NO_SUCH_COMMAND;
}
diff --git a/common/tpm_registers.c b/common/tpm_registers.c
index 444ed52e20..b5c5f8b7e5 100644
--- a/common/tpm_registers.c
+++ b/common/tpm_registers.c
@@ -569,10 +569,21 @@ size_t tpm_get_burst_size(void)
#ifdef CONFIG_EXTENSION_COMMAND
+/* Recognize both original extension and new vendor-specific command codes */
+#define IS_CUSTOM_CODE(code) \
+ ((code == CONFIG_EXTENSION_COMMAND) || \
+ (code & TPM_CC_VENDOR_BIT_MASK))
+
static void call_extension_command(struct tpm_cmd_header *tpmh,
- size_t *total_size)
+ size_t *total_size)
{
size_t command_size = be32toh(tpmh->size);
+ uint32_t rc;
+
+ /*
+ * Note that we don't look for TPM_CC_VENDOR_CR50 anywhere. All
+ * vendor-specific commands are handled the same way for now.
+ */
/* Verify there is room for at least the extension command header. */
if (command_size >= sizeof(struct tpm_cmd_header)) {
@@ -582,14 +593,18 @@ static void call_extension_command(struct tpm_cmd_header *tpmh,
*total_size -= sizeof(struct tpm_cmd_header);
subcommand_code = be16toh(tpmh->subcommand_code);
- extension_route_command(subcommand_code,
- tpmh + 1,
- command_size -
- sizeof(struct tpm_cmd_header),
- total_size);
+ rc = extension_route_command(subcommand_code,
+ tpmh + 1,
+ command_size -
+ sizeof(struct tpm_cmd_header),
+ total_size);
/* Add the header size back. */
*total_size += sizeof(struct tpm_cmd_header);
tpmh->size = htobe32(*total_size);
+ /* Flag errors from commands as vendor-specific */
+ if (rc)
+ rc |= VENDOR_RC_ERR;
+ tpmh->command_code = htobe32(rc);
} else {
*total_size = command_size;
}
@@ -689,7 +704,7 @@ void tpm_task(void)
watchdog_reload();
#ifdef CONFIG_EXTENSION_COMMAND
- if (command_code == CONFIG_EXTENSION_COMMAND) {
+ if (IS_CUSTOM_CODE(command_code)) {
response_size = sizeof(tpm_.regs.data_fifo);
call_extension_command(tpmh, &response_size);
} else
@@ -711,7 +726,7 @@ void tpm_task(void)
if (command_code == TPM2_PCR_Read)
system_process_retry_counter();
#ifdef CONFIG_EXTENSION_COMMAND
- if (command_code != CONFIG_EXTENSION_COMMAND)
+ if (!IS_CUSTOM_CODE(command_code))
#endif
{
/*
diff --git a/include/extension.h b/include/extension.h
index d3001934da..225ca87239 100644
--- a/include/extension.h
+++ b/include/extension.h
@@ -11,6 +11,43 @@
#include "common.h"
+/* Extension and vendor commands. */
+enum vendor_cmd_cc {
+ /* Original extension commands */
+ EXTENSION_AES = 0,
+ EXTENSION_HASH = 1,
+ EXTENSION_RSA = 2,
+ EXTENSION_ECC = 3,
+ EXTENSION_FW_UPGRADE = 4,
+ EXTENSION_HKDF = 5,
+ EXTENSION_ECIES = 6,
+ EXTENSION_POST_RESET = 7,
+
+ LAST_EXTENSION_COMMAND = 15,
+
+ /* Our TPMv2 vendor-specific command codes. 16 bits available. */
+ VENDOR_CC_GET_LOCK = 16,
+ VENDOR_CC_SET_LOCK = 17,
+
+ LAST_VENDOR_COMMAND = 65535,
+};
+
+/* Error codes reported by extension and vendor commands. */
+enum vendor_cmd_rc {
+ /* EXTENSION_HASH error codes */
+ /* Attempt to start a session on an active handle. */
+ EXC_HASH_DUPLICATED_HANDLE = 1,
+ EXC_HASH_TOO_MANY_HANDLES = 2, /* No room to allocate a new context. */
+ /* Continuation/finish on unknown context. */
+ EXC_HASH_UNKNOWN_CONTEXT = 3,
+
+ /* Our TPMv2 vendor-specific response codes. */
+ VENDOR_RC_SUCCESS = 0,
+ VENDOR_RC_BOGUS_ARGS = 1,
+ /* Only 7 bits available; max is 127 */
+ VENDOR_RC_NO_SUCH_COMMAND = 127,
+};
+
/*
* Type of function handling extension commands.
*
@@ -20,12 +57,13 @@
* @param response_size On input - max size of the buffer, on output - actual
* number of data returned by the handler.
*/
-typedef void (*extension_handler)(void *buffer,
- size_t command_size,
- size_t *response_size);
+typedef enum vendor_cmd_rc (*extension_handler)(enum vendor_cmd_cc code,
+ void *buffer,
+ size_t command_size,
+ size_t *response_size);
+
/*
* Find handler for an extension command.
- *
* @param command_code Code associated with a extension command handler.
* @param buffer Data to be processd by the handler, the same space
* is used for data returned by the handler.
@@ -34,42 +72,59 @@ typedef void (*extension_handler)(void *buffer,
* data returned by the handler. A single byte return
* usually indicates an error and contains the error code.
*/
-void extension_route_command(uint16_t command_code,
- void *buffer,
- size_t command_size,
- size_t *size);
+uint32_t extension_route_command(uint16_t command_code,
+ void *buffer,
+ size_t command_size,
+ size_t *size);
+
+/*
+ * The TPMv2 Spec mandates that vendor-specific command codes have bit 29 set,
+ * while bits 15-0 indicate the command. All other bits should be zero.
+ * We will define one of those 16-bit command values for Cr50 purposes, and use
+ * the subcommand_code in struct tpm_cmd_header to further distinguish the
+ * desired operation.
+ */
+#define TPM_CC_VENDOR_BIT_MASK 0x20000000
+#define VENDOR_CC_MASK 0x0000ffff
+/* Our vendor-specific command codes go here */
+#define TPM_CC_VENDOR_CR50 0x0000
+/*
+ * The TPM response code is all zero for success.
+ * Errors are a little complicated:
+ *
+ * Bits 31:12 must be zero.
+ *
+ * Bit 11 S=0 Error
+ * Bit 10 T=1 Vendor defined response code
+ * Bit 9 r=0 reserved
+ * Bit 8 V=1 Conforms to TPMv2 spec
+ * Bit 7 F=0 Confirms to Table 14, Format-Zero Response Codes
+ * Bits 6:0 num 128 possible failure reasons
+ */
+#define VENDOR_RC_ERR 0x00000500
+
+/* Pointer table */
struct extension_command {
uint16_t command_code;
extension_handler handler;
} __packed;
-/* Values for different extension subcommands. */
-enum {
- EXTENSION_AES = 0,
- EXTENSION_HASH = 1,
- EXTENSION_RSA = 2,
- EXTENSION_ECC = 3,
- EXTENSION_FW_UPGRADE = 4,
- EXTENSION_HKDF = 5,
- EXTENSION_ECIES = 6,
- EXTENSION_POST_RESET = 7,
-};
-
-
-/* Error codes reported by extension commands. */
-enum {
- /* EXTENSION_HASH error codes */
- /* Attempt to start a session on an active handle. */
- EXC_HASH_DUPLICATED_HANDLE = 1,
- EXC_HASH_TOO_MANY_HANDLES = 2, /* No room to allocate a new context. */
- /* Continuation/finish on unknown context. */
- EXC_HASH_UNKNOWN_CONTEXT = 3
-};
+#define DECLARE_EXTENSION_COMMAND(code, func) \
+ static enum vendor_cmd_rc func##_wrap(enum vendor_cmd_cc code, \
+ void *cmd_body, \
+ size_t cmd_size, \
+ size_t *response_size) { \
+ func(cmd_body, cmd_size, response_size); \
+ return 0; \
+ } \
+ const struct extension_command __keep __extension_cmd_##code \
+ __attribute__((section(".rodata.extensioncmds"))) \
+ = {.command_code = code, .handler = func##_wrap }
-#define DECLARE_EXTENSION_COMMAND(code, handler) \
- const struct extension_command __keep __extension_cmd_##code \
- __attribute__((section(".rodata.extensioncmds"))) \
- = {code, handler}
+#define DECLARE_VENDOR_COMMAND(code, func) \
+ const struct extension_command __keep __vendor_cmd_##code \
+ __attribute__((section(".rodata.extensioncmds"))) \
+ = {.command_code = code, .handler = func}
#endif /* __EC_INCLUDE_EXTENSION_H */