diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/console.c | 207 | ||||
-rw-r--r-- | common/flash_commands.c | 229 | ||||
-rw-r--r-- | common/host_command.c | 203 | ||||
-rw-r--r-- | common/i8042.c | 137 | ||||
-rw-r--r-- | common/main.c | 94 | ||||
-rw-r--r-- | common/port80.c | 74 | ||||
-rw-r--r-- | common/shared_mem.c | 54 | ||||
-rw-r--r-- | common/util.c | 151 | ||||
-rw-r--r-- | common/vboot.c | 113 |
9 files changed, 1262 insertions, 0 deletions
diff --git a/common/console.c b/common/console.c new file mode 100644 index 0000000000..bc558f5cf4 --- /dev/null +++ b/common/console.c @@ -0,0 +1,207 @@ +/* Copyright (c) 2011 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. + */ + +/* Console module for Chrome EC */ + +#include "console.h" +#include "task.h" +#include "uart.h" +#include "registers.h" +#include "util.h" + +#define MAX_COMMAND_GROUPS 20 +#define MAX_ARGS_PER_COMMAND 10 + +#define PROMPT "> " + +static const struct console_group *group_list[MAX_COMMAND_GROUPS]; +static int group_count = 0; + + +void console_has_input(void) +{ + /* Wake up the console task */ + task_send_msg(TASK_ID_CONSOLE, TASK_ID_CONSOLE, 0); +} + + +int console_register_commands(const struct console_group *group) +{ + if (group_count >= MAX_COMMAND_GROUPS) + return EC_ERROR_OVERFLOW; + + group_list[group_count++] = group; + return EC_SUCCESS; +} + + +/* Splits a line of input into words. Stores the count of words in + * <argc>. Stores pointers to the words in <argv>, which must be at + * least <max_argc> long. If more than <max_argc> words are found, + * discards the excess and returns EC_ERROR_OVERFLOW. */ +int split_words(char *input, int max_argc, int *argc, char **argv) +{ + char *c; + int in_word = 0; + + /* Parse input into words */ + *argc = 0; + for (c = input; *c; c++) { + if (isspace(*c)) { + if (in_word) { + /* Ending a word */ + *c = '\0'; + ++*argc; + in_word = 0; + } + } else if (*c == '#') { + /* After the hash sign is comment, ignored. + * TODO: Need more logic to suuport escaping. */ + break; + } else { + if (!in_word) { + /* Starting a new word */ + if (*argc >= max_argc) + return EC_ERROR_OVERFLOW; + + argv[*argc] = c; + in_word = 1; + } + } + } + return EC_SUCCESS; +} + + +/* Finds a command by name. Returns the command structure, or NULL if + * no match found. */ +const struct console_command *find_command(char *name) +{ + const struct console_command *cmd; + int c, g; + + /* Find the command in the command groups */ + for (g = 0; g < group_count; g++) { + cmd = group_list[g]->commands; + for (c = group_list[g]->command_count; c > 0; c--, cmd++) { + if (!strcasecmp(name, cmd->name)) + return cmd; + } + } + + return NULL; +} + + +/* Handles a line of input containing a single command. + * + * Modifies the input string during parsing. */ +static int handle_command(char *input) +{ + const struct console_command *cmd; + char *argv[MAX_ARGS_PER_COMMAND]; + int argc = 0; + + /* Split input into words. Ignore words past our limit. */ + split_words(input, MAX_ARGS_PER_COMMAND, &argc, argv); + + /* If no command, nothing to do */ + if (!argc) + return EC_SUCCESS; + + cmd = find_command(argv[0]); + if (cmd) + return cmd->handler(argc, argv); + + uart_printf("Command '%s' not found.\n", argv[0]); + return EC_ERROR_UNKNOWN; +} + + +static char last_input[80]; +static char input_buf[80]; + +/* handle a console command */ +void console_process(void) +{ + int rv; + + /* Process all the pending commands. Need to do this all at once + * since our interrupt may have been triggered multiple times. */ + /* TODO: Go to sleep briefly between commands to give lower + * priority tasks a chance to run? */ + while (uart_peek('\n') >= 0) { + uart_gets(input_buf, sizeof(input_buf)); + + /* "." repeats the last command, if any */ + if (!strcasecmp(input_buf, ".\n")) + strzcpy(input_buf, last_input, sizeof(input_buf)); + else if (!isspace(*input_buf)) + strzcpy(last_input, input_buf, sizeof(last_input)); + + rv = handle_command(input_buf); + if (rv != EC_SUCCESS) + uart_printf("Command returned error %d\n", rv); + uart_puts(PROMPT); + } +} + +void console_task(void) +{ + console_init(); + + while (1) { + console_process(); + /* wait for the next command message */ + task_wait_msg(-1); + } +} + +/*****************************************************************************/ +/* Console commands */ + +/* Command handler - prints help. */ +static int command_help(int argc, char **argv) +{ + const struct console_command *cmd; + int c, g; + + uart_puts("Known commands:\n"); + + for (g = 0; g < group_count; g++) { + cmd = group_list[g]->commands; + uart_printf("Group %s:\n", group_list[g]->group_name); + for (c = group_list[g]->command_count; c > 0; c--, cmd++) + uart_printf(" %s\n", cmd->name); + /* Generates enough output to overflow the buffer */ + uart_flush_output(); + } + + uart_puts("'.' repeats the last command.\n"); + + return EC_SUCCESS; +} + +static const struct console_command console_commands[] = { + {"help", command_help}, + {"?", command_help}, +}; +static const struct console_group command_group = { + "Console", console_commands, ARRAY_SIZE(console_commands) +}; + +/*****************************************************************************/ +/* Initialization */ + +int console_init(void) +{ + *last_input = '\0'; + *input_buf = '\0'; + uart_set_console_mode(1); + uart_printf("Console is enabled; type HELP for help.\n"); + uart_puts(PROMPT); + /* Register our internal commands */ + return console_register_commands(&command_group); +} diff --git a/common/flash_commands.c b/common/flash_commands.c new file mode 100644 index 0000000000..c53f8e6d97 --- /dev/null +++ b/common/flash_commands.c @@ -0,0 +1,229 @@ +/* Copyright (c) 2011 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. + */ + +/* Flash memory module for Chrome EC */ + +#include "console.h" +#include "flash.h" +#include "flash_commands.h" +#include "lpc_commands.h" +#include "registers.h" /* TODO: remove; only for temp debugging */ +#include "shared_mem.h" +#include "uart.h" +#include "util.h" + + +/*****************************************************************************/ +/* Console commands */ + +static int command_flash_info(int argc, char **argv) +{ + uart_printf("Usable flash size: %d KB\n", flash_get_size()); + return EC_SUCCESS; +} + + +static int command_flash_erase(int argc, char **argv) +{ + int offset = 0; + int size = FLASH_ERASE_BYTES; + char *endptr; + int rv; + + if (argc < 2) { + uart_puts("Usage: flasherase <offset> [size]\n"); + return EC_ERROR_UNKNOWN; + } + + offset = strtoi(argv[1], &endptr, 0); + if (*endptr) { + uart_puts("Invalid offset\n"); + return EC_ERROR_UNKNOWN; + } + + if (argc > 2) { + size = strtoi(argv[2], &endptr, 0); + if (*endptr) { + uart_puts("Invalid size\n"); + return EC_ERROR_UNKNOWN; + } + } + + uart_printf("Erasing %d bytes at offset 0x%x (%d)...\n", + size, offset, offset); + rv = flash_erase(offset, size); + if (rv == EC_SUCCESS) + uart_puts("done.\n"); + else + uart_printf("failed. (error %d)\n", rv); + + return rv; +} + + +static int command_flash_write(int argc, char **argv) +{ + char *data; + int offset = 0; + int size = 1024; /* Default size */ + char *endptr; + int rv; + int i; + + if (argc < 2) { + uart_puts("Usage: flashwrite <offset> <size>\n"); + return EC_ERROR_UNKNOWN; + } + + offset = strtoi(argv[1], &endptr, 0); + if (*endptr) { + uart_puts("Invalid offset\n"); + return EC_ERROR_UNKNOWN; + } + + if (argc > 2) { + size = strtoi(argv[2], &endptr, 0); + if (*endptr) { + uart_puts("Invalid size\n"); + return EC_ERROR_UNKNOWN; + } + if (size > shared_mem_size()) { + uart_puts("Truncating size\n"); + size = sizeof(data); + } + } + + /* Acquire the shared memory buffer */ + rv = shared_mem_acquire(size, 0, &data); + if (rv) { + uart_printf("Unable to acquire %d byte buffer\n", size); + return rv; + } + + /* Fill the data buffer with a pattern */ + for (i = 0; i < size; i++) + data[i] = i; + + uart_printf("Writing %d bytes to offset 0x%x (%d)...\n", + size, offset, offset); + rv = flash_write(offset, size, data); + if (rv == EC_SUCCESS) + uart_puts("done.\n"); + else + uart_printf("failed. (error %d)\n", rv); + + /* Free the buffer */ + shared_mem_release(data); + + return rv; +} + + +static int command_flash_wp(int argc, char **argv) +{ + int b = 0; + char *endptr; + + if (argc < 2) { + uart_puts("Usage: flashwp [bitmask]\n"); + uart_printf("(current value of FMPPE1: 0x%08x)\n", + LM4_FLASH_FMPPE1); + return EC_SUCCESS; + } + + b = strtoi(argv[1], &endptr, 0); + if (*endptr) { + uart_puts("Invalid bitmask\n"); + return EC_ERROR_UNKNOWN; + } + + uart_printf("FMPPE1 before: 0x%08x\n", LM4_FLASH_FMPPE1); + LM4_FLASH_FMPPE1 = b; + uart_printf("FMPPE1 after: 0x%08x\n", LM4_FLASH_FMPPE1); + return EC_SUCCESS; +} + + +static const struct console_command console_commands[] = { + {"flasherase", command_flash_erase}, + {"flashinfo", command_flash_info}, + {"flashwrite", command_flash_write}, + {"flashwp", command_flash_wp}, +}; +static const struct console_group command_group = { + "Flash", console_commands, ARRAY_SIZE(console_commands) +}; + + + +/*****************************************************************************/ +/* Host commands */ + +enum lpc_status flash_command_get_info(uint8_t *data) +{ + struct lpc_response_flash_info *r = + (struct lpc_response_flash_info *)data; + + r->flash_size = flash_get_size(); + r->write_block_size = FLASH_WRITE_BYTES; + r->erase_block_size = FLASH_ERASE_BYTES; + r->protect_block_size = FLASH_PROTECT_BYTES; + return EC_LPC_STATUS_SUCCESS; +} + + +enum lpc_status flash_command_read(uint8_t *data) +{ + struct lpc_params_flash_read *p = + (struct lpc_params_flash_read *)data; + struct lpc_response_flash_read *r = + (struct lpc_response_flash_read *)data; + + if (p->size > sizeof(r->data)) + return EC_LPC_STATUS_ERROR; + + if (flash_read(p->offset, p->size, r->data)) + return EC_LPC_STATUS_ERROR; + + return EC_LPC_STATUS_SUCCESS; +} + + +enum lpc_status flash_command_write(uint8_t *data) +{ + struct lpc_params_flash_write *p = + (struct lpc_params_flash_write *)data; + + if (p->size > sizeof(p->data)) + return EC_LPC_STATUS_ERROR; + + if (flash_write(p->offset, p->size, p->data)) + return EC_LPC_STATUS_ERROR; + + return EC_LPC_STATUS_SUCCESS; +} + + +enum lpc_status flash_command_erase(uint8_t *data) +{ + struct lpc_params_flash_erase *p = + (struct lpc_params_flash_erase *)data; + + if (flash_erase(p->offset, p->size)) + return EC_LPC_STATUS_ERROR; + + return EC_LPC_STATUS_SUCCESS; +} + +/*****************************************************************************/ +/* Initialization */ + +int flash_commands_init(void) +{ + /* Register our internal commands */ + console_register_commands(&command_group); + return EC_SUCCESS; +} + diff --git a/common/host_command.c b/common/host_command.c new file mode 100644 index 0000000000..1f27196412 --- /dev/null +++ b/common/host_command.c @@ -0,0 +1,203 @@ +/* Copyright (c) 2011 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. + */ + +/* Host command module for Chrome EC */ + +#include "config.h" +#include "console.h" +#include "flash_commands.h" +#include "host_command.h" +#include "lpc.h" +#include "lpc_commands.h" +#include "system.h" +#include "task.h" +#include "uart.h" +#include "registers.h" +#include "util.h" + +static int host_command = 0; +static uint8_t *host_data; + +/*****************************************************************************/ +/* Host commands */ + +void host_command_received(int command) +{ + /* TODO: should warn if we already think we're in a command */ + + /* If this is the reboot command, reboot immediately. This gives + * the host processor a way to unwedge the EC even if it's busy with + * some other command. */ + if (command == EC_LPC_COMMAND_REBOOT) { + system_reset(1); + /* Reset should never return; if it does, post an error */ + lpc_send_host_response(EC_LPC_STATUS_ERROR); + return; + } + + /* Save the command */ + host_command = command; + + /* Wake up the task to handle the command */ + task_send_msg(TASK_ID_HOSTCMD, TASK_ID_HOSTCMD, 0); +} + + +static enum lpc_status HostCommandHello(uint8_t *data) +{ + struct lpc_params_hello *p = (struct lpc_params_hello *)data; + struct lpc_response_hello *r = (struct lpc_response_hello *)data; + uint32_t d = p->in_data; + + uart_printf("[LPC Hello 0x%08x]\n", d); + + /* TODO - remove this delay. For now, pretend command takes a long + * time, so we can see the busy bit set on the host side. */ + { + volatile int i __attribute__((unused)); + int j; + for (j = 0; j < 10000000; j++) + i = j; + } + uart_puts("[LPC sending hello back]\n"); + + r->out_data = d + 0x01020304; + return EC_LPC_STATUS_SUCCESS; +} + + +static enum lpc_status HostCommandGetVersion(uint8_t *data) +{ + struct lpc_response_get_version *r = + (struct lpc_response_get_version *)data; + + uart_printf("[LPC GetVersion]\n"); + + strzcpy(r->version_string_ro, system_get_version(SYSTEM_IMAGE_RO), + sizeof(r->version_string_ro)); + strzcpy(r->version_string_rw_a, system_get_version(SYSTEM_IMAGE_RW_A), + sizeof(r->version_string_rw_a)); + strzcpy(r->version_string_rw_b, system_get_version(SYSTEM_IMAGE_RW_B), + sizeof(r->version_string_rw_b)); + + switch(system_get_image_copy()) { + case SYSTEM_IMAGE_RO: + r->current_image = EC_LPC_IMAGE_RO; + break; + case SYSTEM_IMAGE_RW_A: + r->current_image = EC_LPC_IMAGE_RW_A; + break; + case SYSTEM_IMAGE_RW_B: + r->current_image = EC_LPC_IMAGE_RW_B; + break; + default: + r->current_image = EC_LPC_IMAGE_UNKNOWN; + break; + } + + return EC_LPC_STATUS_SUCCESS; +} + + +static enum lpc_status HostCommandReadTest(uint8_t *data) +{ + struct lpc_params_read_test *p = (struct lpc_params_read_test *)data; + struct lpc_response_read_test *r = + (struct lpc_response_read_test *)data; + + int offset = p->offset; + int size = p->size / sizeof(uint32_t); + int i; + + if (size > ARRAY_SIZE(r->data)) + return EC_LPC_STATUS_ERROR; + + for (i = 0; i < size; i++) + r->data[i] = offset + i; + + return EC_LPC_STATUS_SUCCESS; +} + + +/* handle a LPC command */ +static void command_process(void) +{ + uart_printf("[hostcmd 0x%02x]\n", host_command); + + /* TODO: might be smaller to make this a table, once we get a bunch + * of commands. */ + switch (host_command) { + case EC_LPC_COMMAND_HELLO: + lpc_send_host_response(HostCommandHello(host_data)); + return; + case EC_LPC_COMMAND_GET_VERSION: + lpc_send_host_response(HostCommandGetVersion(host_data)); + return; + case EC_LPC_COMMAND_READ_TEST: + lpc_send_host_response(HostCommandReadTest(host_data)); + return; + case EC_LPC_COMMAND_FLASH_INFO: + lpc_send_host_response(flash_command_get_info(host_data)); + return; + case EC_LPC_COMMAND_FLASH_READ: + lpc_send_host_response(flash_command_read(host_data)); + return; + case EC_LPC_COMMAND_FLASH_WRITE: + lpc_send_host_response(flash_command_write(host_data)); + return; + case EC_LPC_COMMAND_FLASH_ERASE: + lpc_send_host_response(flash_command_erase(host_data)); + return; + default: + lpc_send_host_response(EC_LPC_STATUS_INVALID_COMMAND); + } +} + +/*****************************************************************************/ +/* Console commands */ + +/* Command handler - prints EC version. */ +static int command_version(int argc, char **argv) +{ + uart_printf("RO version: %s\n", + system_get_version(SYSTEM_IMAGE_RO)); + uart_printf("RW-A version: %s\n", + system_get_version(SYSTEM_IMAGE_RW_A)); + uart_printf("RW-B version: %s\n", + system_get_version(SYSTEM_IMAGE_RW_B)); + return EC_SUCCESS; +} + + +static const struct console_command console_commands[] = { + {"version", command_version}, +}; + +static const struct console_group command_group = { + "Host commands", console_commands, ARRAY_SIZE(console_commands) +}; + +/*****************************************************************************/ +/* Initialization */ + +int host_command_init(void) +{ + host_data = (uint8_t *)lpc_get_host_range(); + + console_register_commands(&command_group); + return EC_SUCCESS; +} + +void host_command_task(void) +{ + host_command_init(); + + while (1) { + /* wait for the next command message */ + task_wait_msg(-1); + /* process it */ + command_process(); + } +} diff --git a/common/i8042.c b/common/i8042.c new file mode 100644 index 0000000000..42c6095ecc --- /dev/null +++ b/common/i8042.c @@ -0,0 +1,137 @@ +/* Copyright (c) 2011 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. + * + * Chrome OS EC i8042 interface code. + */ + +#include "board.h" +#include "common.h" +#include "i8042.h" +#include "keyboard.h" +/* TODO: Code in common.c should not directly access chip registers */ +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "uart.h" +#include "util.h" + + +#define I8042_DEBUG 1 + +#define MAX_QUEUED_KEY_PRESS 16 + +/* Circular buffer to host. + * head: next to dequeqe + * tail: next to enqueue + * head == tail: empty. + * tail + 1 == head: full + */ +static int head_to_buffer = 0; +static int tail_to_buffer = 0; +#define HOST_BUFFER_SIZE (16) +static uint8_t to_host_buffer[HOST_BUFFER_SIZE]; + + +void i8042_init() +{ + head_to_buffer = tail_to_buffer = 0; + LM4_LPC_ST(3) = 0; /* clear the TOH bit */ +} + + +void i8042_receives_data(int data) +{ + int ret_len; + uint8_t output[MAX_SCAN_CODE_LEN]; + enum ec_error_list ret; + + ret_len = handle_keyboard_data(data, output); + ret = i8042_send_to_host(ret_len, output); + ASSERT(ret == EC_SUCCESS); +} + + +void i8042_receives_command(int cmd) +{ + int ret_len; + uint8_t output[MAX_SCAN_CODE_LEN]; + enum ec_error_list ret; + + ret_len = handle_keyboard_command(cmd, output); + ret = i8042_send_to_host(ret_len, output); + ASSERT(ret == EC_SUCCESS); +} + + +static void enq_to_host(int len, uint8_t *to_host) +{ + int from, to; + + /* TODO: need atomic protection */ + if ((tail_to_buffer + len) <= (head_to_buffer + HOST_BUFFER_SIZE - 1)) { + for (from = 0, to = tail_to_buffer; from < len;) { + to_host_buffer[to++] = to_host[from++]; + to %= HOST_BUFFER_SIZE; + } + tail_to_buffer = (tail_to_buffer + len) % HOST_BUFFER_SIZE; + } + /* end of atomic protection */ +} + + +void i8042_command_task(void) +{ + while (1) { + /* Either a new byte to host or host picking up can un-block. */ + task_wait_msg(-1); + + while (1) { + uint8_t chr; + int empty = 0; + + /* TODO: need atomic protection */ + if (head_to_buffer == tail_to_buffer) { + empty = 1; /* nothing to host */ + } + /* end of atomic protection */ + if (empty) break; + + /* if the host still didn't read that away, + try next time. */ + if (LM4_LPC_ST(3) & (1 << 0 /* TOH */)) { +#if I8042_DEBUG >= 5 + uart_printf("[%d] i8042_command_task() " + "cannot send to host due to TOH\n", + get_time().le.lo); +#endif + break; + } + + /* TODO: need atomic protection */ + chr = to_host_buffer[head_to_buffer]; + head_to_buffer = + (head_to_buffer + 1) % HOST_BUFFER_SIZE; + /* end of atomic protection */ + + /* Write to host. TOH is set automatically. */ + LPC_POOL_KEYBOARD[1] = chr; +#if I8042_DEBUG >= 4 + uart_printf("[%d] i8042_command_task() " + "sends to host: 0x%02x\n", + get_time().le.lo, chr); +#endif + } + } +} + + +enum ec_error_list i8042_send_to_host(int len, uint8_t *to_host) +{ + enq_to_host(len, to_host); + + /* Wake up the task to handle the command */ + task_send_msg(TASK_ID_I8042CMD, TASK_ID_I8042CMD, 0); + + return EC_SUCCESS; +} diff --git a/common/main.c b/common/main.c new file mode 100644 index 0000000000..c9e1bbe75d --- /dev/null +++ b/common/main.c @@ -0,0 +1,94 @@ +/* Copyright (c) 2011 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. + * Copyright 2011 Google Inc. + * + * Example of EC main loop + */ + +#include "registers.h" +#include "board.h" + +#include "adc.h" +#include "clock.h" +#include "console.h" +#include "eeprom.h" +#include "flash.h" +#include "flash_commands.h" +#include "gpio.h" +#include "i2c.h" +#include "keyboard.h" +#include "lpc.h" +#include "memory_commands.h" +#include "port80.h" +#include "powerdemo.h" +#include "pwm.h" +#include "system.h" +#include "task.h" +#include "temp_sensor.h" +#include "timer.h" +#include "uart.h" +#include "vboot.h" +#include "watchdog.h" + +/* example task blinking the user LED */ +void UserLedBlink(void) +{ + while (1) { + gpio_set(EC_GPIO_DEBUG_LED, 1); + usleep(500000); + watchdog_reload(); + gpio_set(EC_GPIO_DEBUG_LED, 0); + usleep(500000); + watchdog_reload(); + } +} + + +int main(void) +{ + /* Configure the pin multiplexers */ + configure_board(); + /* Set the CPU clocks / PLLs */ + clock_init(); + + /* Do system, gpio, and vboot pre-initialization so we can jump to + * another image if necessary. This must be done as early as + * possible, so that the minimum number of components get + * re-initialized if we jump to another image. */ + system_pre_init(); + gpio_pre_init(); + vboot_pre_init(); + + /* TODO - race condition on enabling interrupts. Module inits + * should call task_IntEnable(int) when they're ready... */ + task_init(); + + watchdog_init(1100); + timer_init(); + uart_init(); + system_init(); + gpio_init(); + flash_init(); + eeprom_init(); + port_80_init(); + lpc_init(); + flash_commands_init(); + vboot_init(); + pwm_init(); + i2c_init(); + temp_sensor_init(); + memory_commands_init(); + keyboard_init(); + adc_init(); + + /* Print the reset cause */ + uart_printf("\n\n--- Chrome EC initialized! ---\n"); + uart_printf("(image: %s, version: %s, last reset: %s)\n", + system_get_image_copy_string(), + system_get_version(SYSTEM_IMAGE_UNKNOWN), + system_get_reset_cause_string()); + + /* Launch task scheduling (never returns) */ + return task_start(); +} diff --git a/common/port80.c b/common/port80.c new file mode 100644 index 0000000000..68d3219aa0 --- /dev/null +++ b/common/port80.c @@ -0,0 +1,74 @@ +/* Copyright (c) 2011 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. + */ + +/* Port 80 module for Chrome EC */ + +#include "console.h" +#include "port80.h" +#include "uart.h" +#include "util.h" + + +#define HISTORY_LEN 16 + +static uint8_t history[HISTORY_LEN]; +static int head = 0; /* Next index to use / oldest previous entry */ +static int last_data = -1; /* Last data written to port 80 */ + + +void port_80_write(int data) +{ + /* Ignore duplicate writes, since the linux kernel writes to port 80 + * as a delay mechanism during boot. */ + if (data == last_data) + return; + + /* TODO: post to SWI and print from there? This currently + * prints from inside the LPC interrupt itself. */ + + uart_printf("[Port 80: 0x%02x]\n", data); + last_data = data; + + history[head] = data; + head = (head + 1) & (HISTORY_LEN - 1); +} + + +/*****************************************************************************/ +/* Console commands */ + +static int command_port80(int argc, char **argv) +{ + int h = head; + int i; + + /* Technically, if a port 80 write comes in while we're + * printing this, we could print an incorrect history. + * Probably not worth the complexity to work around that. */ + + uart_puts("Last port 80 writes:"); + for (i = 0; i < HISTORY_LEN; i++) + uart_printf(" %02x", history[(h + i) & (HISTORY_LEN - 1)]); + uart_puts(" <--newest\n"); + return EC_SUCCESS; +} + + +static const struct console_command console_commands[] = { + {"port80", command_port80}, +}; +static const struct console_group command_group = { + "Port 80", console_commands, ARRAY_SIZE(console_commands) +}; + +/*****************************************************************************/ +/* Initialization */ + +int port_80_init(void) +{ + console_register_commands(&command_group); + memset(history, 0, sizeof(history)); + return EC_SUCCESS; +} diff --git a/common/shared_mem.c b/common/shared_mem.c new file mode 100644 index 0000000000..bff3ec6da3 --- /dev/null +++ b/common/shared_mem.c @@ -0,0 +1,54 @@ +/* Copyright (c) 2011 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 "shared_mem.h" +#include "uart.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; +} + + +int shared_mem_size(void) +{ + return SHARED_MEM_SIZE; +} + + +int shared_mem_acquire(int size, int wait, char **dest_ptr) +{ + if (size > SHARED_MEM_SIZE || size <= 0) + return EC_ERROR_INVAL; + + /* TODO: if task_start() hasn't been called, fail immediately + * if not available. */ + + /* TODO: wait if requested; for now, we fail immediately if + * not available. */ + if (buf_in_use) + return EC_ERROR_BUSY; + + /* TODO: atomically acquire buf_in_use. */ + buf_in_use = 1; + *dest_ptr = shared_buf; + return EC_SUCCESS; +} + + +void shared_mem_release(void *ptr) +{ + /* TODO: use event to wake up a previously-blocking acquire */ + buf_in_use = 0; +} diff --git a/common/util.c b/common/util.c new file mode 100644 index 0000000000..5eefcbe6ff --- /dev/null +++ b/common/util.c @@ -0,0 +1,151 @@ +/* Copyright (c) 2011 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. + */ + +/* Utility functions for Chrome EC */ + +#include "util.h" + +int strlen(const char *s) +{ + int len = 0; + + while (*s++) + len++; + + return len; +} + + +int isspace(int c) +{ + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; +} + + +int isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + + +int tolower(int c) +{ + return c >= 'A' && c <= 'Z' ? c + 'a' - 'A' : c; +} + + +int strcasecmp(const char *s1, const char *s2) +{ + int diff; + do { + diff = tolower(*s1) - tolower(*s2); + if (diff) + return diff; + } while (*(s1++) && *(s2++)); + return 0; +} + + +int atoi(const char *nptr) +{ + int result = 0; + int neg = 0; + char c = '\0'; + + while ((c = *nptr++) && isspace(c)) {} + + if (c == '-') { + neg = 1; + c = *nptr++; + } + + while (isdigit(c)) { + result = result * 10 + (c - '0'); + c = *nptr++; + } + + return neg ? -result : result; +} + + +/* Like strtol(), but for integers */ +int strtoi(const char *nptr, char **endptr, int base) +{ + int result = 0; + int neg = 0; + int c = '\0'; + + if (endptr) + *endptr = (char *)nptr; + + while((c = *nptr++) && isspace(c)) {} + + if (c == '0' && *nptr == 'x' && (base == 0 || base == 16)) { + base = 16; + c = nptr[1]; + nptr += 2; + } else { + base = 10; + if (c == '-') { + neg = 1; + c = *nptr++; + } + } + + while (c) { + if (c >= '0' && c < '0' + MIN(base, 10)) + result = result * base + (c - '0'); + else if (c >= 'A' && c < 'A' + base - 10) + result = result * base + (c - 'A' + 10); + else if (c >= 'a' && c < 'a' + base - 10) + result = result * base + (c - 'a' + 10); + else + break; + + if (endptr) + *endptr = (char *)nptr; + c = *nptr++; + } + + return neg ? -result : result; +} + + +void *memcpy(void *dest, const void *src, int len) +{ + /* TODO: optimized version using LDM/STM would be much faster */ + char *d = (char *)dest; + const char *s = (const char *)src; + while (len > 0) { + *(d++) = *(s++); + len--; + } + return dest; +} + + +void *memset(void *dest, int c, int len) +{ + /* TODO: optimized version using STM would be much faster */ + char *d = (char *)dest; + while (len > 0) { + *(d++) = c; + len--; + } + return dest; +} + + +/* Like strncpy(), but guarantees null termination */ +char *strzcpy(char *dest, const char *src, int len) +{ + char *d = dest; + while (len > 1 && *src) { + *(d++) = *(src++); + len--; + } + *d = '\0'; + return dest; +} diff --git a/common/vboot.c b/common/vboot.c new file mode 100644 index 0000000000..ebf514d79d --- /dev/null +++ b/common/vboot.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2011 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. + */ + +/* Verified boot module for Chrome EC */ + +#include "console.h" +#include "system.h" +#include "uart.h" +#include "util.h" +#include "vboot.h" + + +#define SCRATCHPAD_EMPTY 0 +#define SCRATCHPAD_REQUEST_A 0xb00daaaa +#define SCRATCHPAD_REQUEST_B 0xb00dbbbb +#define SCRATCHPAD_SELECTED_A 0x0000d1da +#define SCRATCHPAD_SELECTED_B 0x0000d1db +#define SCRATCHPAD_SELECTED_RO 0x0000d1d0 +#define SCRATCHPAD_FAILED_A 0x0000eeea +#define SCRATCHPAD_FAILED_B 0x0000eeeb + + +/* Jumps to one of the RW images if necessary. */ +static void jump_to_other_image(void) +{ + int s; + + if (system_get_image_copy() != SYSTEM_IMAGE_RO) + return; /* Not in RO firmware, so ignore scratchpad */ + + if (system_get_reset_cause() != SYSTEM_RESET_SOFT_COLD) { + /* In RO firmware, but not because of a warm boot. + * Stay in RO regardless of scratchpad, and clear it + * so we don't use it on the next boot. */ + system_set_scratchpad(SCRATCHPAD_EMPTY); + return; + } + + /* TODO: check recovery button; if it's pressed, stay in RO */ + + /* Check for a scratchpad value we recognize. Clear the + * scratchpad before jumping, so we only do this once. */ + s = system_get_scratchpad(); + if (s == SCRATCHPAD_REQUEST_A) { + system_set_scratchpad(SCRATCHPAD_SELECTED_A); + system_run_image_copy(SYSTEM_IMAGE_RW_A); + /* Shouldn't normally return; if we did, flag error */ + system_set_scratchpad(SCRATCHPAD_FAILED_A); + } else if (s == SCRATCHPAD_REQUEST_B) { + system_set_scratchpad(SCRATCHPAD_SELECTED_B); + system_run_image_copy(SYSTEM_IMAGE_RW_B); + /* Shouldn't normally return; if we did, flag error */ + system_set_scratchpad(SCRATCHPAD_FAILED_B); + } else { + system_set_scratchpad(SCRATCHPAD_EMPTY); + } +} + + +/*****************************************************************************/ +/* Console commands */ + +static int command_reboot(int argc, char **argv) +{ + /* Handle request to boot to a specific image */ + if (argc >= 2) { + if (!strcasecmp(argv[1], "a")) { + uart_puts("Rebooting to image A!\n\n\n"); + system_set_scratchpad(SCRATCHPAD_REQUEST_A); + } else if (!strcasecmp(argv[1], "b")) { + uart_puts("Rebooting to image B!\n\n\n"); + system_set_scratchpad(SCRATCHPAD_REQUEST_B); + } else { + uart_puts("Usage: reboot [ A | B ]\n"); + return EC_ERROR_UNKNOWN; + } + } else { + uart_puts("Rebooting to RO!\n\n\n"); + } + + uart_flush_output(); + /* TODO - param to specify warm/cold */ + system_reset(1); + return EC_SUCCESS; +} + +static const struct console_command console_commands[] = { + {"reboot", command_reboot}, +}; + +static const struct console_group command_group = { + "Verified boot", console_commands, ARRAY_SIZE(console_commands) +}; + +/*****************************************************************************/ +/* Initialization */ + +int vboot_pre_init(void) +{ + /* Jump to a different image if necessary; this may not return */ + jump_to_other_image(); + return EC_SUCCESS; +} + + +int vboot_init(void) +{ + /* Register our internal commands */ + console_register_commands(&command_group); + return EC_SUCCESS; +} |