| /* 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. |
| */ |
| |
| /* System module for Chrome EC : common functions */ |
| |
| #include "board.h" |
| #include "clock.h" |
| #include "config.h" |
| #include "console.h" |
| #include "ec_commands.h" |
| #include "flash.h" |
| #include "gpio.h" |
| #include "hooks.h" |
| #include "host_command.h" |
| #include "lpc.h" |
| #include "panic.h" |
| #include "system.h" |
| #include "task.h" |
| #include "uart.h" |
| #include "util.h" |
| #include "version.h" |
| |
| /* Console output macros */ |
| #define CPUTS(outstr) cputs(CC_SYSTEM, outstr) |
| #define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args) |
| |
| 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 3 |
| #define JUMP_DATA_SIZE_V2 16 /* Size of version 2 jump data struct */ |
| 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. |
| */ |
| |
| /* Fields from version 3 */ |
| uint8_t reserved0; /* (used in proto1 to signal recovery mode) */ |
| int struct_size; /* Size of struct jump_data */ |
| |
| /* Fields from version 2 */ |
| int jump_tag_total; /* Total size of all jump tags */ |
| |
| /* Fields from version 1 */ |
| uint32_t reset_flags; /* Reset flags from 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 (at end of RAM, or preceding panic data) */ |
| static struct jump_data *jdata; |
| |
| /* |
| * Reset flag descriptions. Must be in same order as bits of RESET_FLAG_ |
| * constants. |
| */ |
| static const char * const reset_flag_descs[] = { |
| "other", "reset-pin", "brownout", "power-on", "watchdog", "soft", |
| "hibernate", "rtc-alarm", "wake-pin", "low-battery", "sysjump", |
| "hard", "ap-off", "preserved"}; |
| |
| static const char * const image_names[] = {"unknown", "RO", "RW"}; |
| static uint32_t reset_flags; |
| static int jumped_to_image; |
| static int disable_jump; /* Disable ALL jumps if system is locked */ |
| static int force_locked; /* Force system locked even if WP isn't enabled */ |
| static enum ec_reboot_cmd reboot_at_shutdown; |
| |
| int system_is_locked(void) |
| { |
| if (force_locked) |
| return 1; |
| |
| #ifdef CONFIG_SYSTEM_UNLOCKED |
| /* System is explicitly unlocked */ |
| return 0; |
| |
| #elif defined(CONFIG_FLASH) |
| /* |
| * Unlocked if write protect pin deasserted or read-only firmware |
| * is not protected. |
| */ |
| if ((EC_FLASH_PROTECT_GPIO_ASSERTED | EC_FLASH_PROTECT_RO_NOW) & |
| ~flash_get_protect()) |
| return 0; |
| |
| /* If WP pin is asserted and lock is applied, we're locked */ |
| return 1; |
| #else |
| /* Other configs are locked by default */ |
| return 1; |
| #endif |
| } |
| |
| |
| int system_usable_ram_end(void) |
| { |
| /* Leave space at the end of RAM for jump data and tags. |
| * |
| * 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; |
| } |
| |
| uint32_t system_get_reset_flags(void) |
| { |
| return reset_flags; |
| } |
| |
| void system_set_reset_flags(uint32_t flags) |
| { |
| reset_flags |= flags; |
| } |
| |
| void system_clear_reset_flags(uint32_t flags) |
| { |
| reset_flags &= ~flags; |
| } |
| |
| void system_print_reset_flags(void) |
| { |
| int count = 0; |
| int i; |
| |
| if (!reset_flags) { |
| uart_puts("unknown"); |
| return; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(reset_flag_descs); i++) { |
| if (reset_flags & (1 << i)) { |
| if (count++) |
| uart_puts(" "); |
| |
| uart_puts(reset_flag_descs[i]); |
| } |
| } |
| } |
| |
| int system_jumped_to_this_image(void) |
| { |
| return jumped_to_image; |
| } |
| |
| 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 || 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; |
| |
| if (!jdata) |
| return NULL; |
| |
| /* 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_disable_jump(void) |
| { |
| disable_jump = 1; |
| } |
| |
| enum system_image_copy_t system_get_image_copy(void) |
| { |
| uint32_t my_addr = (uint32_t)system_get_image_copy - CONFIG_FLASH_BASE; |
| |
| if (my_addr >= CONFIG_SECTION_RO_OFF && |
| my_addr < (CONFIG_SECTION_RO_OFF + CONFIG_SECTION_RO_SIZE)) |
| return SYSTEM_IMAGE_RO; |
| |
| if (my_addr >= CONFIG_SECTION_RW_OFF && |
| my_addr < (CONFIG_SECTION_RW_OFF + CONFIG_SECTION_RW_SIZE)) |
| return SYSTEM_IMAGE_RW; |
| |
| return SYSTEM_IMAGE_UNKNOWN; |
| } |
| |
| int system_get_image_used(enum system_image_copy_t copy) |
| { |
| const uint8_t *image; |
| int size = 0; |
| |
| if (copy == SYSTEM_IMAGE_RO) { |
| image = (const uint8_t *)CONFIG_SECTION_RO_OFF; |
| size = CONFIG_SECTION_RO_SIZE; |
| } else if (copy == SYSTEM_IMAGE_RW) { |
| image = (const uint8_t *)CONFIG_SECTION_RW_OFF; |
| size = CONFIG_SECTION_RW_SIZE; |
| } |
| |
| if (size <= 0) |
| return 0; |
| |
| /* |
| * Scan backwards looking for 0xea byte, which is by definition the |
| * last byte of the image. See ec.lds.S for how this is inserted at |
| * the end of the image. |
| */ |
| for (size--; size > 0 && image[size] != 0xea; size--) |
| ; |
| |
| return size ? size + 1 : 0; /* 0xea byte IS part of the image */ |
| } |
| |
| /* Returns true if the given range is overlapped with the active image. |
| * |
| * We only care the runtime code since the EC is running over it. |
| * We don't care about the vector table, FMAP, and init code. */ |
| int system_unsafe_to_overwrite(uint32_t offset, uint32_t size) { |
| uint32_t r_offset; |
| uint32_t r_size; |
| |
| switch (system_get_image_copy()) { |
| case SYSTEM_IMAGE_RO: |
| r_offset = CONFIG_FW_RO_OFF; |
| r_size = CONFIG_FW_RO_SIZE; |
| break; |
| case SYSTEM_IMAGE_RW: |
| r_offset = CONFIG_FW_RW_OFF; |
| r_size = CONFIG_FW_RW_SIZE; |
| break; |
| default: |
| return 0; |
| } |
| |
| if ((offset >= r_offset && offset < (r_offset + r_size)) || |
| (r_offset >= offset && r_offset < (offset + size))) |
| return 1; |
| else |
| return 0; |
| } |
| |
| |
| const char *system_get_image_copy_string(void) |
| { |
| int copy = system_get_image_copy(); |
| return copy < ARRAY_SIZE(image_names) ? image_names[copy] : "?"; |
| } |
| |
| |
| /* Jump to what we hope is the init address of an image. This function does |
| * not return. */ |
| static void jump_to_image(uint32_t init_addr) |
| { |
| void (*resetvec)(void) = (void(*)(void))init_addr; |
| |
| /* |
| * Jumping to any image asserts the signal to the Silego chip that that |
| * EC is not in read-only firmware. (This is not technically true if |
| * jumping from RO -> RO, but that's not a meaningful use case...) |
| */ |
| gpio_set_level(GPIO_ENTERING_RW, 1); |
| |
| /* Flush UART output unless the UART hasn't been initialized yet */ |
| if (uart_init_done()) |
| uart_flush_output(); |
| |
| /* Disable interrupts before jump */ |
| interrupt_disable(); |
| |
| /* Fill in preserved data between jumps */ |
| jdata->reserved0 = 0; |
| jdata->magic = JUMP_DATA_MAGIC; |
| jdata->version = JUMP_DATA_VERSION; |
| jdata->reset_flags = reset_flags; |
| jdata->jump_tag_total = 0; /* Reset tags */ |
| jdata->struct_size = sizeof(struct jump_data); |
| |
| /* Call other hooks; these may add tags */ |
| hook_notify(HOOK_SYSJUMP, 0); |
| |
| /* Jump to the reset vector */ |
| resetvec(); |
| } |
| |
| |
| /* Return the base pointer for the image copy, or 0xffffffff if error. */ |
| static uint32_t get_base(enum system_image_copy_t copy) |
| { |
| switch (copy) { |
| case SYSTEM_IMAGE_RO: |
| return CONFIG_FLASH_BASE + CONFIG_FW_RO_OFF; |
| case SYSTEM_IMAGE_RW: |
| return CONFIG_FLASH_BASE + CONFIG_FW_RW_OFF; |
| default: |
| return 0xffffffff; |
| } |
| } |
| |
| /* Return the size of the image copy, or 0 if error. */ |
| static uint32_t get_size(enum system_image_copy_t copy) |
| { |
| switch (copy) { |
| case SYSTEM_IMAGE_RO: |
| return CONFIG_FW_RO_SIZE; |
| case SYSTEM_IMAGE_RW: |
| return CONFIG_FW_RW_SIZE; |
| default: |
| return 0; |
| } |
| } |
| |
| |
| int system_run_image_copy(enum system_image_copy_t copy) |
| { |
| uint32_t base; |
| uint32_t init_addr; |
| |
| /* If system is already running the requested image, done */ |
| if (system_get_image_copy() == copy) |
| return EC_SUCCESS; |
| |
| if (system_is_locked()) { |
| /* System is locked, so disallow jumping between images unless |
| * this is the initial jump from RO to RW code. */ |
| |
| /* Must currently be running the RO image */ |
| if (system_get_image_copy() != SYSTEM_IMAGE_RO) |
| return EC_ERROR_ACCESS_DENIED; |
| |
| /* Target image must be RW image */ |
| if (copy != SYSTEM_IMAGE_RW) |
| return EC_ERROR_ACCESS_DENIED; |
| |
| /* Can't have already jumped between images */ |
| if (jumped_to_image) |
| return EC_ERROR_ACCESS_DENIED; |
| |
| /* Jumping must still be enabled */ |
| if (disable_jump) |
| return EC_ERROR_ACCESS_DENIED; |
| } |
| |
| /* Load the appropriate reset vector */ |
| base = get_base(copy); |
| if (base == 0xffffffff) |
| return EC_ERROR_INVAL; |
| |
| /* Make sure the reset vector is inside the destination image */ |
| init_addr = *(uint32_t *)(base + 4); |
| if (init_addr < base || init_addr >= base + get_size(copy)) |
| return EC_ERROR_UNKNOWN; |
| |
| CPRINTF("[%T Jumping to image %s]\n", image_names[copy]); |
| |
| jump_to_image(init_addr); |
| |
| /* Should never get here */ |
| return EC_ERROR_UNKNOWN; |
| } |
| |
| |
| const char *system_get_version(enum system_image_copy_t copy) |
| { |
| uint32_t addr; |
| const struct version_struct *v; |
| |
| /* Handle version of current image */ |
| if (copy == system_get_image_copy() || copy == SYSTEM_IMAGE_UNKNOWN) |
| return version_data.version; |
| |
| addr = get_base(copy); |
| if (addr == 0xffffffff) |
| return ""; |
| |
| /* The version string is always located after the reset vectors, so |
| * it's the same as in the current image. */ |
| addr += ((uint32_t)&version_data - get_base(system_get_image_copy())); |
| |
| /* Make sure the version struct cookies match before returning the |
| * version string. */ |
| v = (const struct version_struct *)addr; |
| if (v->cookie1 == version_data.cookie1 && |
| v->cookie2 == version_data.cookie2) |
| return v->version; |
| |
| return ""; |
| } |
| |
| |
| int system_get_board_version(void) |
| { |
| int v = 0; |
| |
| #ifdef BOARD_link |
| if (gpio_get_level(GPIO_BOARD_VERSION1)) |
| v |= 0x01; |
| if (gpio_get_level(GPIO_BOARD_VERSION2)) |
| v |= 0x02; |
| if (gpio_get_level(GPIO_BOARD_VERSION3)) |
| v |= 0x04; |
| #endif |
| |
| return v; |
| } |
| |
| |
| const char *system_get_build_info(void) |
| { |
| return build_info; |
| } |
| |
| int system_common_pre_init(void) |
| { |
| uint32_t addr; |
| |
| /* |
| * Put the jump data before the panic data, or at the end of RAM if |
| * panic data is not present. |
| */ |
| addr = (uint32_t)panic_get_data(); |
| if (!addr) |
| addr = CONFIG_RAM_BASE + CONFIG_RAM_SIZE; |
| |
| jdata = (struct jump_data *)(addr - sizeof(struct jump_data)); |
| |
| /* |
| * Check jump data if this is a jump between images. Jumps all show up |
| * as an unknown reset reason, because we jumped directly from one |
| * image to another without actually triggering a chip reset. |
| */ |
| if (jdata->magic == JUMP_DATA_MAGIC && |
| jdata->version >= 1 && |
| reset_flags == 0) { |
| int delta; /* Change in jump data struct size between the |
| * previous image and this one. */ |
| |
| /* Yes, we jumped to this image */ |
| jumped_to_image = 1; |
| /* Restore the reset flags */ |
| reset_flags = jdata->reset_flags | RESET_FLAG_SYSJUMP; |
| |
| /* |
| * If the jump data structure isn't the same size as the |
| * current one, shift the jump tags to immediately before the |
| * current jump data structure, to make room for initalizing |
| * the new fields below. |
| */ |
| if (jdata->version == 1) |
| delta = 0; /* No tags in v1, so no need for move */ |
| else if (jdata->version == 2) |
| delta = sizeof(struct jump_data) - JUMP_DATA_SIZE_V2; |
| else |
| delta = sizeof(struct jump_data) - jdata->struct_size; |
| |
| if (delta && jdata->jump_tag_total) { |
| uint8_t *d = (uint8_t *)system_usable_ram_end(); |
| memmove(d, d + delta, jdata->jump_tag_total); |
| } |
| |
| /* Initialize fields added after version 1 */ |
| if (jdata->version < 2) |
| jdata->jump_tag_total = 0; |
| |
| /* Initialize fields added after version 2 */ |
| if (jdata->version < 3) |
| jdata->reserved0 = 0; |
| |
| /* Struct size is now the current struct size */ |
| jdata->struct_size = sizeof(struct jump_data); |
| |
| /* |
| * 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; |
| } |
| |
| /* Handle a pending reboot command */ |
| static int handle_pending_reboot(enum ec_reboot_cmd cmd) |
| { |
| switch (cmd) { |
| case EC_REBOOT_CANCEL: |
| return EC_SUCCESS; |
| case EC_REBOOT_JUMP_RO: |
| return system_run_image_copy(SYSTEM_IMAGE_RO); |
| case EC_REBOOT_JUMP_RW: |
| return system_run_image_copy(SYSTEM_IMAGE_RW); |
| case EC_REBOOT_COLD: |
| system_reset(SYSTEM_RESET_HARD); |
| /* That shouldn't return... */ |
| return EC_ERROR_UNKNOWN; |
| case EC_REBOOT_DISABLE_JUMP: |
| system_disable_jump(); |
| return EC_SUCCESS; |
| default: |
| return EC_ERROR_INVAL; |
| } |
| } |
| |
| /*****************************************************************************/ |
| /* Hooks */ |
| |
| static int system_common_shutdown(void) |
| { |
| return handle_pending_reboot(reboot_at_shutdown); |
| } |
| DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, system_common_shutdown, HOOK_PRIO_DEFAULT); |
| |
| /*****************************************************************************/ |
| /* Console commands */ |
| |
| static int command_sysinfo(int argc, char **argv) |
| { |
| ccprintf("Reset flags: 0x%08x (", system_get_reset_flags()); |
| system_print_reset_flags(); |
| ccprintf(")\n"); |
| ccprintf("Copy: %s\n", system_get_image_copy_string()); |
| ccprintf("Jumped: %s\n", system_jumped_to_this_image() ? "yes" : "no"); |
| |
| ccputs("Flags: "); |
| if (system_is_locked()) { |
| ccputs(" locked"); |
| if (force_locked) |
| ccputs(" (forced)"); |
| if (disable_jump) |
| ccputs(" jump-disabled"); |
| } else |
| ccputs(" unlocked"); |
| ccputs("\n"); |
| |
| if (reboot_at_shutdown) |
| ccprintf("Reboot at shutdown: %d\n", reboot_at_shutdown); |
| |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(sysinfo, command_sysinfo, |
| NULL, |
| "Print system info", |
| NULL); |
| |
| |
| #define CONFIG_CONSOLE_CMD_SCRATCHPAD |
| #ifdef CONFIG_CONSOLE_CMD_SCRATCHPAD |
| static int command_scratchpad(int argc, char **argv) |
| { |
| int rv = EC_SUCCESS; |
| |
| if (argc == 2) { |
| char *e; |
| int s = strtoi(argv[1], &e, 0); |
| if (*e) |
| return EC_ERROR_PARAM1; |
| rv = system_set_scratchpad(s); |
| } |
| |
| ccprintf("Scratchpad: 0x%08x\n", system_get_scratchpad()); |
| return rv; |
| } |
| DECLARE_CONSOLE_COMMAND(scratchpad, command_scratchpad, |
| "[val]", |
| "Get or set scratchpad value", |
| NULL); |
| #endif /* CONFIG_CONSOLE_CMD_SCRATCHPAD */ |
| |
| |
| static int command_hibernate(int argc, char **argv) |
| { |
| int seconds = 0; |
| int microseconds = 0; |
| |
| if (argc >= 2) |
| seconds = strtoi(argv[1], NULL, 0); |
| if (argc >= 3) |
| microseconds = strtoi(argv[2], NULL, 0); |
| |
| if (seconds || microseconds) |
| ccprintf("Hibernating for %d.%06d s\n", seconds, microseconds); |
| else |
| ccprintf("Hibernating until wake pin asserted.\n"); |
| |
| system_hibernate(seconds, microseconds); |
| |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(hibernate, command_hibernate, |
| "[sec] [usec]", |
| "Hibernate the EC", |
| NULL); |
| |
| |
| static int command_version(int argc, char **argv) |
| { |
| ccprintf("Chip: %s %s %s\n", system_get_chip_vendor(), |
| system_get_chip_name(), system_get_chip_revision()); |
| ccprintf("Board: %d\n", system_get_board_version()); |
| ccprintf("RO: %s\n", system_get_version(SYSTEM_IMAGE_RO)); |
| ccprintf("RW: %s\n", system_get_version(SYSTEM_IMAGE_RW)); |
| ccprintf("Build: %s\n", system_get_build_info()); |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(version, command_version, |
| NULL, |
| "Print versions", |
| NULL); |
| |
| |
| static int command_sysjump(int argc, char **argv) |
| { |
| uint32_t addr; |
| char *e; |
| |
| if (argc < 2) |
| return EC_ERROR_PARAM_COUNT; |
| |
| /* Handle named images */ |
| if (!strcasecmp(argv[1], "RO")) |
| return system_run_image_copy(SYSTEM_IMAGE_RO); |
| else if (!strcasecmp(argv[1], "RW") || !strcasecmp(argv[1], "A")) { |
| /* TODO: remove "A" once all scripts are updated to use "RW" */ |
| return system_run_image_copy(SYSTEM_IMAGE_RW); |
| } else if (!strcasecmp(argv[1], "disable")) { |
| system_disable_jump(); |
| return EC_SUCCESS; |
| } |
| |
| /* Arbitrary jumps are only allowed on an unlocked system */ |
| if (system_is_locked()) |
| return EC_ERROR_ACCESS_DENIED; |
| |
| /* Check for arbitrary address */ |
| addr = strtoi(argv[1], &e, 0); |
| if (*e) |
| return EC_ERROR_PARAM1; |
| |
| ccprintf("Jumping to 0x%08x\n", addr); |
| cflush(); |
| jump_to_image(addr); |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(sysjump, command_sysjump, |
| "[RO | RW | addr | disable]", |
| "Jump to a system image or address", |
| NULL); |
| |
| |
| static int command_reboot(int argc, char **argv) |
| { |
| int flags = 0; |
| int i; |
| |
| for (i = 1; i < argc; i++) { |
| if (!strcasecmp(argv[i], "hard") || |
| !strcasecmp(argv[i], "cold")) { |
| flags |= SYSTEM_RESET_HARD; |
| } else if (!strcasecmp(argv[i], "soft")) { |
| flags &= ~SYSTEM_RESET_HARD; |
| } else if (!strcasecmp(argv[i], "ap-off")) { |
| flags |= SYSTEM_RESET_LEAVE_AP_OFF; |
| } else if (!strcasecmp(argv[i], "cancel")) { |
| reboot_at_shutdown = EC_REBOOT_CANCEL; |
| return EC_SUCCESS; |
| } else if (!strcasecmp(argv[i], "preserve")) { |
| flags |= SYSTEM_RESET_PRESERVE_FLAGS; |
| } else |
| return EC_ERROR_PARAM1 + i - 1; |
| } |
| |
| if (flags & SYSTEM_RESET_HARD) |
| ccputs("Hard-"); |
| ccputs("Rebooting!\n\n\n"); |
| cflush(); |
| system_reset(flags); |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(reboot, command_reboot, |
| "[hard|soft] [preserve] [ap-off] [cancel]", |
| "Reboot the EC", |
| NULL); |
| |
| static int command_system_lock(int argc, char **argv) |
| { |
| force_locked = 1; |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(syslock, command_system_lock, |
| NULL, |
| "Lock the system, even if WP is disabled", |
| NULL); |
| |
| /*****************************************************************************/ |
| /* Host commands */ |
| |
| static int host_command_get_version(struct host_cmd_handler_args *args) |
| { |
| struct ec_response_get_version *r = args->response; |
| |
| strzcpy(r->version_string_ro, system_get_version(SYSTEM_IMAGE_RO), |
| sizeof(r->version_string_ro)); |
| strzcpy(r->version_string_rw, system_get_version(SYSTEM_IMAGE_RW), |
| sizeof(r->version_string_rw)); |
| |
| switch (system_get_image_copy()) { |
| case SYSTEM_IMAGE_RO: |
| r->current_image = EC_IMAGE_RO; |
| break; |
| case SYSTEM_IMAGE_RW: |
| r->current_image = EC_IMAGE_RW; |
| break; |
| default: |
| r->current_image = EC_IMAGE_UNKNOWN; |
| break; |
| } |
| |
| args->response_size = sizeof(*r); |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_GET_VERSION, |
| host_command_get_version, |
| EC_VER_MASK(0)); |
| |
| static int host_command_build_info(struct host_cmd_handler_args *args) |
| { |
| const char *info = system_get_build_info(); |
| int len; |
| |
| len = MIN(strlen(info), args->response_max - 1); |
| args->response_size = args->response_max; |
| memcpy(args->response, info, len); |
| memset(args->response + len, 0, args->response_size - len); |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_GET_BUILD_INFO, |
| host_command_build_info, |
| EC_VER_MASK(0)); |
| |
| static int host_command_get_chip_info(struct host_cmd_handler_args *args) |
| { |
| struct ec_response_get_chip_info *r = args->response; |
| |
| strzcpy(r->vendor, system_get_chip_vendor(), sizeof(r->vendor)); |
| strzcpy(r->name, system_get_chip_name(), sizeof(r->name)); |
| strzcpy(r->revision, system_get_chip_revision(), sizeof(r->revision)); |
| |
| args->response_size = sizeof(*r); |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_GET_CHIP_INFO, |
| host_command_get_chip_info, |
| EC_VER_MASK(0)); |
| |
| int host_command_get_board_version(struct host_cmd_handler_args *args) |
| { |
| struct ec_response_board_version *r = args->response; |
| |
| r->board_version = (uint16_t) system_get_board_version(); |
| |
| args->response_size = sizeof(*r); |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_GET_BOARD_VERSION, |
| host_command_get_board_version, |
| EC_VER_MASK(0)); |
| |
| int host_command_vbnvcontext(struct host_cmd_handler_args *args) |
| { |
| const struct ec_params_vbnvcontext *p = args->params; |
| struct ec_response_vbnvcontext *r; |
| |
| switch (p->op) { |
| case EC_VBNV_CONTEXT_OP_READ: |
| r = args->response; |
| if (system_get_vbnvcontext(r->block)) |
| return EC_RES_ERROR; |
| args->response_size = sizeof(*r); |
| break; |
| case EC_VBNV_CONTEXT_OP_WRITE: |
| if (system_set_vbnvcontext(p->block)) |
| return EC_RES_ERROR; |
| break; |
| default: |
| return EC_RES_ERROR; |
| } |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_VBNV_CONTEXT, |
| host_command_vbnvcontext, |
| EC_VER_MASK(EC_VER_VBNV_CONTEXT)); |
| |
| int host_command_reboot(struct host_cmd_handler_args *args) |
| { |
| struct ec_params_reboot_ec p; |
| |
| /* |
| * Ensure reboot parameters don't get clobbered when the response |
| * is sent in case data argument points to the host tx/rx buffer. |
| */ |
| memcpy(&p, args->params, sizeof(p)); |
| |
| if (p.cmd == EC_REBOOT_CANCEL) { |
| /* Cancel pending reboot */ |
| reboot_at_shutdown = EC_REBOOT_CANCEL; |
| return EC_RES_SUCCESS; |
| } else if (p.flags & EC_REBOOT_FLAG_ON_AP_SHUTDOWN) { |
| /* Store request for processing at chipset shutdown */ |
| reboot_at_shutdown = p.cmd; |
| return EC_RES_SUCCESS; |
| } |
| |
| /* TODO: (crosbug.com/p/9040) handle EC_REBOOT_FLAG_POWER_ON */ |
| |
| #ifdef CONFIG_TASK_HOSTCMD |
| /* Clean busy bits on host */ |
| args->result = EC_RES_SUCCESS; |
| host_send_response(args); |
| #endif |
| |
| CPRINTF("[%T Executing host reboot command %d]\n", p.cmd); |
| switch (handle_pending_reboot(p.cmd)) { |
| case EC_SUCCESS: |
| return EC_RES_SUCCESS; |
| case EC_ERROR_INVAL: |
| return EC_RES_INVALID_PARAM; |
| case EC_ERROR_ACCESS_DENIED: |
| return EC_RES_ACCESS_DENIED; |
| default: |
| return EC_RES_ERROR; |
| } |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_REBOOT_EC, |
| host_command_reboot, |
| EC_VER_MASK(0)); |