From 513ff559cd6fedd29412fb59b6f436f617620511 Mon Sep 17 00:00:00 2001 From: AppaRao Puli Date: Tue, 21 May 2019 00:53:04 +0530 Subject: [PATCH] FFUJ: FW IPMI commands and flash support in u-boot Firmware update and OEM ipmi commands implementation for supporting Force Firmware Update Jumper(FFUJ) mode. Also added support to update the fit images in FFUJ mode. Firmware update commands: 1) Get BMC Execution Context(0x23) 2) Get Firmware Update Random Number(0x26) 3) Set Firmware Update Mode(0x27) 4) Exit Firmware Update Mode(0x28) 5) Set/Get Firmware Update Control(0x29) 6) Get Firmware Update status(0x2A) 7) Set Firmware Update Options(0x2B) 8) Firmware Image Write(0x2C) OEM Commands: 1) Get Buffer Size(0x66) Tested: - Used cmdtool.efi to test the individual commands implementation and negative cases. - Used debug fwpiaupd.efi tool for validating Firmware image transfer via KCS and flashing. Signed-off-by: AppaRao Puli --- arch/arm/include/asm/arch-aspeed/ast-g5-intel.h | 1 + board/aspeed/ast-g5/Makefile | 2 + board/aspeed/ast-g5/fw-update.c | 486 ++++++++++++++++++++++++ board/aspeed/ast-g5/fw-update.h | 50 +++ board/aspeed/ast-g5/ipmi-fwupd.c | 402 ++++++++++++++++++++ board/aspeed/ast-g5/ipmi-fwupd.h | 81 ++++ board/aspeed/ast-g5/ipmi-handler.c | 66 +++- board/aspeed/ast-g5/ipmi-handler.h | 3 +- common/autoboot.c | 11 + configs/ast_g5_phy_defconfig | 1 + 10 files changed, 1091 insertions(+), 12 deletions(-) create mode 100644 board/aspeed/ast-g5/fw-update.c create mode 100644 board/aspeed/ast-g5/fw-update.h create mode 100644 board/aspeed/ast-g5/ipmi-fwupd.c create mode 100644 board/aspeed/ast-g5/ipmi-fwupd.h diff --git a/arch/arm/include/asm/arch-aspeed/ast-g5-intel.h b/arch/arm/include/asm/arch-aspeed/ast-g5-intel.h index cd9a099..a88521a 100644 --- a/arch/arm/include/asm/arch-aspeed/ast-g5-intel.h +++ b/arch/arm/include/asm/arch-aspeed/ast-g5-intel.h @@ -14,6 +14,7 @@ #ifndef __ASSEMBLY__ int intel_force_firmware_jumper_enabled(void); +void start_fw_update_loop(void); #endif #endif /* __AST_INTEL_G5_H__ */ diff --git a/board/aspeed/ast-g5/Makefile b/board/aspeed/ast-g5/Makefile index f28fcfe..0b2d936 100644 --- a/board/aspeed/ast-g5/Makefile +++ b/board/aspeed/ast-g5/Makefile @@ -6,3 +6,5 @@ obj-y += ast-g5-gpio.o obj-y += ast-g5-timer.o obj-y += ast-g5-kcs.o obj-y += ipmi-handler.o +obj-y += ipmi-fwupd.o +obj-y += fw-update.o diff --git a/board/aspeed/ast-g5/fw-update.c b/board/aspeed/ast-g5/fw-update.c new file mode 100644 index 0000000..9923993 --- /dev/null +++ b/board/aspeed/ast-g5/fw-update.c @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2018-2019 Intel Corporation + +#include +#include +#include + +#include "fw-update.h" + +#define BOOTCMD_BOOTM_STR "bootm " +#define RANDOM_NUM_TIMEOUT 30 /* in seconds */ +#define WAIT_STATE_TIMEOUT 10000 /* 10 seconds */ + +#define PROTECT_OFF 0 +#define PROTECT_ON 1 + +extern struct fwupd_global_setting g_fwupd_settings; +extern u32 g_write_addr; + +bool g_fwupd_settings_lock = false; +unsigned long long etime; + +bool fwupd_settings_trylock(void) +{ + if (g_fwupd_settings_lock) + return false; + + g_fwupd_settings_lock = true; + return g_fwupd_settings_lock; +} + +void fwupd_settings_unlock(void) +{ + g_fwupd_settings_lock = false; +} + +u8 get_active_boot_image(void) +{ + char *bootcmd = getenv("bootcmd"); + char *start = strstr(bootcmd, BOOTCMD_BOOTM_STR); + u8 boot_image = PRIMARY_IMAGE; + + if (start) { + ulong boot_addr = simple_strtoul( + (start + strlen(BOOTCMD_BOOTM_STR)), NULL, 16); + if (boot_addr == SECONDARY_FITIMAGE_START_ADDR) + return SECONDARY_IMAGE; + } + return boot_image; +} + +static ulong get_flash_image_address(void) +{ + char *bootcmd = getenv("bootcmd"); + char *start = strstr(bootcmd, BOOTCMD_BOOTM_STR); + ulong boot_addr = PRIMARY_FITIMAGE_START_ADDR; + + if (start) { + boot_addr = simple_strtoul((start + strlen(BOOTCMD_BOOTM_STR)), + NULL, 16); + /* We update in backup region and set the bootcmd accordingly */ + if (boot_addr == PRIMARY_FITIMAGE_START_ADDR) + boot_addr = SECONDARY_FITIMAGE_START_ADDR; + else + boot_addr = PRIMARY_FITIMAGE_START_ADDR; + } + + return boot_addr; +} + +static void update_processing_status(u8 status, u8 percent) +{ + if (!fwupd_settings_trylock()) + return; + + g_fwupd_settings.processing_status = status; + g_fwupd_settings.percentage_completion = percent; + + fwupd_settings_unlock(); + return; +} + +static void reset_all_settings(void) +{ + if (!fwupd_settings_trylock()) + return; + + memset(&g_fwupd_settings, 0, sizeof(g_fwupd_settings)); + g_fwupd_settings.fwupd_mode_active = false; + g_fwupd_settings.start_update = false; + + fwupd_settings_unlock(); +} + +unsigned int get_seed(void) +{ + char seed_str[] = { "INTEL" }; + unsigned int seed; + + for (int i = 0; i < strlen(seed_str); i++) + seed += (seed_str[i] << (i * 8)); + + return seed; +} + +int generate_random_number(void) +{ + srand(get_seed()); + + if (!fwupd_settings_trylock()) { + printf("%s(): Lock failed\n", __func__); + return -1; + } + for (int i = 0; i < RAND_NUMBER_SIZE; i++) + g_fwupd_settings.rand_num[i] = (u8)(rand() & 0xFF); + + g_fwupd_settings.random_number_valid = true; + + fwupd_settings_unlock(); + + /* Random number should be cleared after 30sec timeout */ + etime = endtick(RANDOM_NUM_TIMEOUT); + + return 0; +} + +static int sect_roundb(ulong *addr) +{ + flash_info_t *info; + ulong bank, sector_end_addr; + char found; + int i; + + /* find the end addr of the sector where the *addr is */ + found = 0; + for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS && !found; ++bank) { + info = &flash_info[bank]; + for (i = 0; i < info->sector_count && !found; ++i) { + /* get the end address of the sector */ + if (i == info->sector_count - 1) { + sector_end_addr = + info->start[0] + info->size - 1; + } else { + sector_end_addr = info->start[i + 1] - 1; + } + + if (*addr <= sector_end_addr && + *addr >= info->start[i]) { + found = 1; + /* adjust *addr if necessary */ + if (*addr < sector_end_addr) + *addr = sector_end_addr; + } /* sector */ + } /* bank */ + } + if (!found) { + /* error, address not in flash */ + printf("Error: end address (0x%08lx) not in flash!\n", *addr); + return 1; + } + + return 0; +} + +static int fill_flash_sect_ranges(ulong addr_first, ulong addr_last, + int *s_first, int *s_last, int *s_count) +{ + flash_info_t *info; + ulong bank; + int rcode = 0; + + *s_count = 0; + + for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank) { + s_first[bank] = -1; /* first sector to erase */ + s_last[bank] = -1; /* last sector to erase */ + } + + for (bank = 0, info = &flash_info[0]; + (bank < CONFIG_SYS_MAX_FLASH_BANKS) && (addr_first <= addr_last); + ++bank, ++info) { + ulong b_end; + int sect; + short s_end; + + if (info->flash_id == FLASH_UNKNOWN) + continue; + + b_end = info->start[0] + info->size - 1; /* bank end addr */ + s_end = info->sector_count - 1; /* last sector */ + + for (sect = 0; sect < info->sector_count; ++sect) { + ulong end; /* last address in current sect */ + + end = (sect == s_end) ? b_end : + info->start[sect + 1] - 1; + + if (addr_first > end) + continue; + if (addr_last < info->start[sect]) + continue; + + if (addr_first == info->start[sect]) + s_first[bank] = sect; + if (addr_last == end) + s_last[bank] = sect; + } + if (s_first[bank] >= 0) { + if (s_last[bank] < 0) { + if (addr_last > b_end) { + s_last[bank] = s_end; + } else { + printf("Error: end address not on sector boundary\n"); + rcode = 1; + break; + } + } + if (s_last[bank] < s_first[bank]) { + printf("Error: end sector precedes start sector\n"); + rcode = 1; + break; + } + sect = s_last[bank]; + addr_first = (sect == s_end) ? b_end + 1 : + info->start[sect + 1]; + (*s_count) += s_last[bank] - s_first[bank] + 1; + } else if (addr_first >= info->start[0] && addr_first < b_end) { + printf("Error: start address not on sector boundary\n"); + rcode = 1; + break; + } else if (s_last[bank] >= 0) { + printf("Error: cannot span across banks when they are mapped in reverse order\n"); + rcode = 1; + break; + } + } + + return rcode; +} + +static int protect_flash_sector(int state, ulong addr_first, ulong addr_last) +{ + flash_info_t *info; + ulong bank; + int s_first[CONFIG_SYS_MAX_FLASH_BANKS], + s_last[CONFIG_SYS_MAX_FLASH_BANKS]; + int protected = 0; + int planned; + int rcode, i; + + rcode = fill_flash_sect_ranges(addr_first, addr_last, s_first, s_last, + &planned); + + if (planned && (rcode == 0)) { + for (bank = 0, info = &flash_info[0]; + bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank, ++info) { + if (info->flash_id == FLASH_UNKNOWN) + continue; + + if (s_first[bank] >= 0 && + s_first[bank] <= s_last[bank]) { + debug("%sProtecting sectors %d..%d in bank %ld\n", + state ? "" : "Un-", s_first[bank], + s_last[bank], bank + 1); + protected + += s_last[bank] - s_first[bank] + 1; + for (i = s_first[bank]; i <= s_last[bank]; ++i) + info->protect[i] = state; + } + } + printf("%sProtected %d sectors\n", state ? "" : "Un-", + protected); + } else if (rcode == 0) { + printf("Error: start and/or end address not on sector boundary\n"); + rcode = 1; + } + + return rcode; +} + +static int erase_flash_sector(ulong addr_first, ulong addr_last) +{ + flash_info_t *info; + ulong bank; + int s_first[CONFIG_SYS_MAX_FLASH_BANKS]; + int s_last[CONFIG_SYS_MAX_FLASH_BANKS]; + int erased = 0; + int planned; + int rcode = 0; + + rcode = fill_flash_sect_ranges(addr_first, addr_last, s_first, s_last, + &planned); + + if (planned && (rcode == 0)) { + for (bank = 0, info = &flash_info[0]; + (bank < CONFIG_SYS_MAX_FLASH_BANKS) && (rcode == 0); + ++bank, ++info) { + if (s_first[bank] >= 0) { + erased += s_last[bank] - s_first[bank] + 1; + debug("Erase Flash from 0x%08lx to 0x%08lx " + "in Bank # %ld ", + info->start[s_first[bank]], + (s_last[bank] == info->sector_count) ? + info->start[0] + info->size - 1 : + info->start[s_last[bank] + 1] - 1, + bank + 1); + rcode = flash_erase(info, s_first[bank], + s_last[bank]); + } + } + if (rcode == 0) + printf("Erased %d sectors\n", erased); + } else if (rcode == 0) { + printf("Error: start and/or end address not on sector boundary\n"); + rcode = 1; + } + + return rcode; +} + +static int verify_image(void) +{ + ulong src_addr = IMAGE_LOAD_RAM_ADDR; + void *hdr = (void *)src_addr; + + printf("\n## Checking Image at 0x%08lx ...\n", src_addr); + /* AT the moment, we only support FIT image flash */ + switch (genimg_get_format(hdr)) { + case IMAGE_FORMAT_FIT: + printf(" FIT image found\n"); + if (!fit_check_format(hdr)) { + printf("Bad FIT image format!\n"); + return -1; + } + + if (!fit_all_image_verify(hdr)) { + printf("Bad hash in FIT image!\n"); + return -1; + } + break; + default: + printf("Unknown image format!\n"); + return -1; + } + + return 0; +} + +static int flash_image(void) +{ + int rcode; + ulong max_size = MAX_FITIMAGE_SIZE; + ulong src_addr = IMAGE_LOAD_RAM_ADDR; + ulong addr_first = get_flash_image_address(); + ulong addr_last = addr_first + max_size - 1; + + if ((g_write_addr > max_size) || (g_write_addr == 0)) { + printf("ERROR: %s(): Invalid file uploaded. filesize(0x%08x)\n", + __func__, g_write_addr); + return -1; + } + + if (sect_roundb(&addr_last) > 0) { + printf("ERROR: %s(): sect_roundb failed\n", __func__); + return -1; + } + + if (addr_first >= addr_last) { + printf("ERROR: %s(): addr_first(0x%08lx) >= addr_last(0x%08lx)\n", + __func__, addr_first, addr_last); + return -1; + } + + /* Hack: To update the percentage update, + * treat logical division as below. + * Image verify - 10% + * Unprotecting flash sectors - 10% + * Erase flash sectors - 40% + * Copy to flash - 40% */ + + /* Unprotect the flash sectors */ + rcode = protect_flash_sector(PROTECT_OFF, addr_first, addr_last); + if (rcode != 0) { + printf("%s(): Protecting flash sector failed(%d).\n", __func__, + rcode); + return -1; + } + update_processing_status(IMG_PROGRAMMING, 20); + + /* erase flash sectors */ + rcode = erase_flash_sector(addr_first, addr_last); + if (rcode != 0) { + printf("%s(): Erasing flash sector failed(%d).\n", __func__, + rcode); + return -1; + } + update_processing_status(IMG_PROGRAMMING, 60); + + /* write to flash area */ + printf("Copy to Flash... "); + rcode = flash_write((char *)src_addr, addr_first, g_write_addr * 1); + if (rcode != 0) { + printf("%s(): Flash copy failed(%d).\n", __func__, rcode); + flash_perror(rcode); + return -1; + } + printf("done\n"); + return 0; +} + +void start_fw_update_loop(void) +{ + int rc; + ulong boot_addr; + char boot_cmd[20]; + + while (1) { + if (g_fwupd_settings.random_number_valid) { + /* Random number should be cleared after 30seconds */ + if (get_ticks() >= etime) { + printf("Clearing random number\n"); + + if (!fwupd_settings_trylock()) + continue; + memcpy(g_fwupd_settings.rand_num, 0, + RAND_NUMBER_SIZE); + g_fwupd_settings.random_number_valid = false; + fwupd_settings_unlock(); + } + } + + if (g_fwupd_settings.start_update) { + update_processing_status(IMG_VALIDATING, 0); + + rc = verify_image(); + if (rc != 0) { + update_processing_status(UPDATE_ERROR, 100); + /* Adding delay to make consumer gets status */ + mdelay(WAIT_STATE_TIMEOUT); + + reset_all_settings(); + continue; + } + + update_processing_status(IMG_PROGRAMMING, 10); + + rc = flash_image(); + if (rc == 0) { + /* Update successful, change the boot command */ + boot_addr = get_flash_image_address(); + snprintf(boot_cmd, sizeof(boot_cmd), + "bootm %08x", boot_addr); + setenv("bootcmd", boot_cmd); + saveenv(); + + update_processing_status(UPDATE_SUCCESSFUL, + 100); + } else { + update_processing_status(UPDATE_ERROR, 100); + } + + /* Adding delay to make sure consumer gets status */ + mdelay(WAIT_STATE_TIMEOUT); + + reset_all_settings(); + + /* Reset BMC */ + do_reset(NULL, 0, 0, NULL); + } + mdelay(WAIT_STATE_TIMEOUT); + } + + return; +} + +#if 1 /* Debug purpose */ +int do_fwupd(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + if (argc != 1) + return 1; + + start_fw_update_loop(); + return 0; +} +U_BOOT_CMD(fwupd, 1, 0, do_fwupd, "Start Firmware update process", ""); +#endif diff --git a/board/aspeed/ast-g5/fw-update.h b/board/aspeed/ast-g5/fw-update.h new file mode 100644 index 0000000..ed033ad --- /dev/null +++ b/board/aspeed/ast-g5/fw-update.h @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018-2019 Intel Corporation + +#include + +/* SPI flash map */ +#define MAX_FITIMAGE_SIZE 0x1B80000 +#define PRIMARY_FITIMAGE_START_ADDR 0x20080000 +#define SECONDARY_FITIMAGE_START_ADDR 0x22480000 +#define IMAGE_LOAD_RAM_ADDR 0x83000000 + +#define MAX_FILENAME_LENGTH 256 +#define RAND_NUMBER_SIZE 8 + +enum boot_image { + PRIMARY_IMAGE = 0x01, + SECONDARY_IMAGE = 0x02 +}; + +enum update_status { + INITIALIZING = 0, + IDLE, + IMG_DOWNLOADING, + IMG_VALIDATING, + IMG_PROGRAMMING, + UPDATE_SUCCESSFUL, + UPDATE_ERROR = 0x0F, + UPDATE_FORBIDDEN = 0x80, + AC_CYCLE_REQUIRED = 0x83 +}; + +struct fwupd_global_setting { + bool fwupd_mode_active; + bool start_update; + bool random_number_valid; + u8 ctrl_state; + u8 options_mask; + u8 options_value; + u8 processing_status; + u8 percentage_completion; + u8 integrity_check_status; + u8 filename_len; + u8 filename[MAX_FILENAME_LENGTH]; + u8 rand_num[RAND_NUMBER_SIZE]; +}; + +bool fwupd_settings_trylock(void); +void fwupd_settings_unlock(void); +u8 get_active_boot_image(void); +int generate_random_number(void); diff --git a/board/aspeed/ast-g5/ipmi-fwupd.c b/board/aspeed/ast-g5/ipmi-fwupd.c new file mode 100644 index 0000000..3eba056 --- /dev/null +++ b/board/aspeed/ast-g5/ipmi-fwupd.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018-2019 Intel Corporation + +#include "ipmi-fwupd.h" + +struct fwupd_global_setting g_fwupd_settings; +u32 g_write_addr = 0; + +u16 fwupd_get_execution_ctx(u8 *req, u16 req_len, u8 *res) +{ + int booting_image = 0x01; + struct fwupd_get_exe_ctx_res *result = + (struct fwupd_get_exe_ctx_res *)res; + + /* Get active image location(primary/secondary) */ + booting_image = get_active_boot_image(); + result->patition_ptr = booting_image; + result->exection_ctx = 0x11; /* Forced Firmware Update mode */ + + result->completion_code = IPMI_CC_OK; + return sizeof(struct fwupd_get_exe_ctx_res); +} +u16 fwupd_get_rand_number(u8 *req, u16 req_len, u8 *res) +{ + struct fwupd_rand_num_res *result = (struct fwupd_rand_num_res *)res; + + if (req_len != 0) { + printf("%s(): Invalid request length\n", __func__); + result->completion_code = IPMI_CC_INVALID_DATA_LENGTH; + return sizeof(result->completion_code); + } + + /* Check is critical operation is going on */ + if (g_fwupd_settings.start_update) { + printf("%s(): Update in progress.\n", __func__); + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + + /* Check is it already in fwupdate mode */ + if (g_fwupd_settings.fwupd_mode_active) { + printf("%s(): Already in firmware update mode\n", __func__); + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + + if (generate_random_number() != 0) { + printf("%s(): Random number generation failed\n", __func__); + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + + result->completion_code = IPMI_CC_OK; + memcpy(result->rand_num, g_fwupd_settings.rand_num, RAND_NUMBER_SIZE); + + return sizeof(struct fwupd_rand_num_res); +} + +u16 fwupd_enter_update_mode(u8 *req, u16 req_len, u8 *res) +{ + struct fwupd_set_update_mode_res *result = + (struct fwupd_set_update_mode_res *)res; + + if (req_len != RAND_NUMBER_SIZE) { + printf("%s(): Invalid request length\n", __func__); + result->completion_code = IPMI_CC_INVALID_DATA_LENGTH; + return sizeof(result->completion_code); + } + + /* Check is critical operation is going on */ + if (g_fwupd_settings.start_update) { + printf("%s(): Update in progress.\n", __func__); + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + + /* Check is it already in fwupdate mode */ + if (g_fwupd_settings.fwupd_mode_active) { + printf("%s(): Already in firmware update mode\n", __func__); + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + + /* This command should excute within 30 seconds + * after random number generation. */ + if (!g_fwupd_settings.random_number_valid) { + printf("%s(): No valid random number exist.\n", __func__); + result->completion_code = IPMI_CC_INVALID_CODE; + return sizeof(result->completion_code); + } + + /* Validate the key to enter this mode */ + for (int i = 0; i < RAND_NUMBER_SIZE; i++) { + if (req[i] != g_fwupd_settings.rand_num[i]) { + printf("%s(): Invalid key entered\n", __func__); + result->completion_code = IPMI_CC_INVALID_CODE; + return sizeof(result->completion_code); + } + } + + if (!fwupd_settings_trylock()) { + printf("%s(): Lock failed\n", __func__); + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + /* Reset all the settings */ + memset(&g_fwupd_settings, 0, sizeof(g_fwupd_settings)); + g_fwupd_settings.fwupd_mode_active = true; + fwupd_settings_unlock(); + + result->completion_code = IPMI_CC_OK; + + return sizeof(struct fwupd_set_update_mode_res); +} + +u16 fwupd_exit_update_mode(u8 *req, u16 req_len, u8 *res) +{ + struct fwupd_exit_update_mode_res *result = + (struct fwupd_exit_update_mode_res *)res; + + if (req_len != 0) { + printf("%s(): Invalid request length\n", __func__); + result->completion_code = IPMI_CC_INVALID_DATA_LENGTH; + return sizeof(result->completion_code); + } + + if (!g_fwupd_settings.fwupd_mode_active) { + printf("%s(): Invalid command entered\n", __func__); + result->completion_code = IPMI_CC_NOT_SUPPORTED_IN_STATE; + return sizeof(result->completion_code); + } + + if (!fwupd_settings_trylock()) { + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + g_fwupd_settings.fwupd_mode_active = false; + fwupd_settings_unlock(); + + result->completion_code = IPMI_CC_OK; + + return sizeof(struct fwupd_exit_update_mode_res); +} +u16 fwupd_set_options(u8 *req, u16 req_len, u8 *res) +{ + struct fwupd_options_req *options_req = (struct fwupd_options_req *)req; + struct fwupd_options_res *result = (struct fwupd_options_res *)res; + + if (req_len < 2) { + printf("%s(): Invalid request length\n", __func__); + result->completion_code = IPMI_CC_INVALID_DATA_LENGTH; + return sizeof(result->completion_code); + } + + /* Check is critical operation is going on */ + if (g_fwupd_settings.start_update) { + printf("%s(): Update in progress.\n", __func__); + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + + /* Setting any reserved bits will result the command being rejected */ + if (((options_req->options_mask & 0xF0) != 0) || + ((options_req->options_value & 0xF0) != 0)) { + printf("%s(): Invalid request\n", __func__); + result->completion_code = IPMI_CC_INVALID_DATA_FIELD; + return sizeof(result->completion_code); + } + + if (!fwupd_settings_trylock()) { + printf("%s(): Lock failed\n", __func__); + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + g_fwupd_settings.options_mask = options_req->options_mask; + g_fwupd_settings.options_value = options_req->options_value; + fwupd_settings_unlock(); + + result->completion_code = IPMI_CC_OK; + result->options_value = (g_fwupd_settings.options_mask & + g_fwupd_settings.options_value); + + return sizeof(struct fwupd_options_res); +} + +u16 fwupd_set_get_control(u8 *req, u16 req_len, u8 *res) +{ + struct fwupd_control_req *ctrl_req = (struct fwupd_control_req *)req; + struct fwupd_control_res *result = (struct fwupd_control_res *)res; + + if (req_len < 1) { + printf("%s(): Invalid request length\n", __func__); + result->completion_code = IPMI_CC_INVALID_DATA_LENGTH; + return sizeof(result->completion_code); + } + + /* Check is critical operation is going on */ + if (g_fwupd_settings.start_update) { + printf("%s(): Update in progress.\n", __func__); + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + + if ((ctrl_req->ctrl_state == SET_FW_FILENAME) && (req_len < 3)) { + printf("%s(): Invalid request data\n", __func__); + result->completion_code = IPMI_CC_INVALID_DATA_LENGTH; + return sizeof(result->completion_code); + } else if ((ctrl_req->ctrl_state != SET_FW_FILENAME) && + (req_len != 1)) { + printf("%s(): Invalid request data\n", __func__); + result->completion_code = IPMI_CC_INVALID_DATA_LENGTH; + return sizeof(result->completion_code); + } + + if ((!g_fwupd_settings.fwupd_mode_active) && + (ctrl_req->ctrl_state != GET_CTRL_STATE)) { + printf("%s(): Invalid request. Control State: %d.\n", __func__, + ctrl_req->ctrl_state); + result->completion_code = IPMI_CC_NOT_SUPPORTED_IN_STATE; + return sizeof(result->completion_code); + } + + switch (ctrl_req->ctrl_state) { + case GET_CTRL_STATE: + break; + case IMG_TRANSFER_START: + if ((g_fwupd_settings.ctrl_state & + IMG_TRANSFER_CTRL_BIT_START)) { + printf("%s(): Invalid request\n", __func__); + result->completion_code = + IPMI_CC_NOT_SUPPORTED_IN_STATE; + return sizeof(result->completion_code); + } + + if (!fwupd_settings_trylock()) { + printf("%s(): Lock failed\n", __func__); + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + g_fwupd_settings.processing_status = IMG_DOWNLOADING; + /* Reset control state during start */ + g_fwupd_settings.ctrl_state = 0x00; + g_fwupd_settings.ctrl_state |= IMG_TRANSFER_CTRL_BIT_START; + /* Set current write address to ZERO */ + g_write_addr = 0x00; + fwupd_settings_unlock(); + break; + case IMG_TRANSFER_END: + if (!(g_fwupd_settings.ctrl_state & + IMG_TRANSFER_CTRL_BIT_START)) { + printf("%s(): Invalid request\n", __func__); + result->completion_code = + IPMI_CC_NOT_SUPPORTED_IN_STATE; + return sizeof(result->completion_code); + } + + if (!fwupd_settings_trylock()) { + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + g_fwupd_settings.start_update = true; + g_fwupd_settings.ctrl_state |= IMG_TRANSFER_CTRL_BIT_END; + + g_fwupd_settings.ctrl_state &= ~(IMG_TRANSFER_CTRL_BIT_START | + IMG_TRANSFER_CTRL_BIT_ABORT); + fwupd_settings_unlock(); + break; + case IMG_TRANSFER_ABORT: + if (!(g_fwupd_settings.ctrl_state & + IMG_TRANSFER_CTRL_BIT_START)) { + printf("%s(): Invalid request\n", __func__); + result->completion_code = + IPMI_CC_NOT_SUPPORTED_IN_STATE; + return sizeof(result->completion_code); + } + + if (!fwupd_settings_trylock()) { + printf("%s(): Lock failed\n", __func__); + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + g_fwupd_settings.processing_status = UPDATE_ERROR; + g_fwupd_settings.ctrl_state |= IMG_TRANSFER_CTRL_BIT_ABORT; + g_fwupd_settings.ctrl_state &= ~(IMG_TRANSFER_CTRL_BIT_START | + IMG_TRANSFER_CTRL_BIT_END); + fwupd_settings_unlock(); + break; + case SET_FW_FILENAME: + /* Not supporting now */ + if (ctrl_req->filename_len > sizeof(ctrl_req->filename)) { + printf("%s(): Invalid request\n", __func__); + result->completion_code = IPMI_CC_INVALID_DATA_FIELD; + return sizeof(result->completion_code); + } + + if (!(g_fwupd_settings.ctrl_state & + IMG_TRANSFER_CTRL_BIT_START)) { + printf("%s(): Invalid request\n", __func__); + result->completion_code = + IPMI_CC_NOT_SUPPORTED_IN_STATE; + return sizeof(result->completion_code); + } + + if (!fwupd_settings_trylock()) { + printf("%s(): Lock failed\n", __func__); + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + g_fwupd_settings.filename_len = ctrl_req->filename_len; + strncpy(g_fwupd_settings.filename, ctrl_req->filename, + ctrl_req->filename_len); + fwupd_settings_unlock(); + /* TODO: Used for TFTP update but not implemented yet. */ + /* TODO: Verify image and write to flash */ + break; + case USB_DEV_ATTACH: + /* Not supporting now */ + result->completion_code = IPMI_CC_NOT_SUPPORTED_IN_STATE; + return sizeof(result->completion_code); + case USB_DEV_DETACH: + /* Not supporting now */ + result->completion_code = IPMI_CC_NOT_SUPPORTED_IN_STATE; + return sizeof(result->completion_code); + break; + default: + printf("%s(): Invalid request\n", __func__); + result->completion_code = IPMI_CC_INVALID_DATA_FIELD; + return sizeof(result->completion_code); + } + + result->completion_code = IPMI_CC_OK; + result->curr_state = g_fwupd_settings.ctrl_state; + return sizeof(struct fwupd_control_res); +} +u16 fwupd_get_update_status(u8 *req, u16 req_len, u8 *res) +{ + struct fwupd_get_update_status_res *result = + (struct fwupd_get_update_status_res *)res; + + if (req_len != 0) { + printf("%s(): Invalid request length\n", __func__); + result->completion_code = IPMI_CC_INVALID_DATA_LENGTH; + return sizeof(result->completion_code); + } + + result->processing_status = g_fwupd_settings.processing_status; + result->percent_completion = g_fwupd_settings.percentage_completion; + result->check_status = 0; + /* We don't support error code messages cmd(0x0EH) in uboot.*/ + result->error_code = 0; + + result->completion_code = IPMI_CC_OK; + + return sizeof(struct fwupd_get_update_status_res); +} + +u16 fwupd_image_write(u8 *req, u16 req_len, u8 *res) +{ + struct fwupd_image_write_res *result = + (struct fwupd_image_write_res *)res; + + if (req_len < 1) { + printf("%s(): Invalid request length\n", __func__); + result->completion_code = IPMI_CC_INVALID_DATA_LENGTH; + return sizeof(result->completion_code); + } + + /* Check is critical operation is going on */ + if (g_fwupd_settings.start_update) { + printf("%s(): Update in progress.\n", __func__); + result->completion_code = IPMI_CC_NODE_BUSY; + return sizeof(result->completion_code); + } + + if (!g_fwupd_settings.fwupd_mode_active) { + printf("%s(): Invalid request\n", __func__); + result->completion_code = IPMI_CC_NOT_SUPPORTED_IN_STATE; + return sizeof(result->completion_code); + } + + if (!(g_fwupd_settings.ctrl_state & IMG_TRANSFER_CTRL_BIT_START)) { + printf("%s(): Invalid request\n", __func__); + result->completion_code = IPMI_CC_NOT_SUPPORTED_IN_STATE; + return sizeof(result->completion_code); + } + + if ((g_write_addr + req_len) > MAX_FITIMAGE_SIZE) { + printf("%s(): Request length exceeded max size\n", __func__); + result->completion_code = IPMI_CC_INVALID_DATA_LENGTH; + return sizeof(result->completion_code); + } + + u8 *mem_addr = (u8 *)((u32)IMAGE_LOAD_RAM_ADDR + g_write_addr); + + memcpy(mem_addr, req, req_len); + g_write_addr += req_len; + + result->completion_code = IPMI_CC_OK; + result->no_of_bytes_written = (u8)req_len; + + return sizeof(struct fwupd_image_write_res); +} diff --git a/board/aspeed/ast-g5/ipmi-fwupd.h b/board/aspeed/ast-g5/ipmi-fwupd.h new file mode 100644 index 0000000..e490f6b --- /dev/null +++ b/board/aspeed/ast-g5/ipmi-fwupd.h @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018-2019 Intel Corporation + +#include "ipmi-handler.h" +#include "fw-update.h" + +enum control_state { + GET_CTRL_STATE = 0, + IMG_TRANSFER_START, + IMG_TRANSFER_END, + IMG_TRANSFER_ABORT, + SET_FW_FILENAME, + USB_DEV_ATTACH, + USB_DEV_DETACH +}; +enum control_state_bit { + IMG_TRANSFER_CTRL_BIT_START = (0x01 << 0), + IMG_TRANSFER_CTRL_BIT_END = (0x01 << 1), + IMG_TRANSFER_CTRL_BIT_ABORT = (0x01 << 2), + USB_CTRL_BIT_ATTACH = (0x01 << 3) +}; +enum update_options_bit { + NO_DOWN_REVISION = 0, + DEFER_BMC_RESET = 1, + SHA32_INTEGRITY_CHECK = 2, + CRC32_INTEGRITY_CHECK = 3 +}; + +struct fwupd_get_exe_ctx_res { + u8 completion_code; + u8 exection_ctx; + u8 patition_ptr; +}; +struct fwupd_rand_num_res { + u8 completion_code; + u8 rand_num[RAND_NUMBER_SIZE]; +}; +struct fwupd_set_update_mode_res { + u8 completion_code; +}; +struct fwupd_exit_update_mode_res { + u8 completion_code; +}; +struct fwupd_options_req { + u8 options_mask; + u8 options_value; + u8 integrity_check_value[32]; +}; +struct fwupd_options_res { + u8 completion_code; + u8 options_value; +}; +struct fwupd_control_req { + u8 ctrl_state; + u8 filename_len; + u8 filename[MAX_FILENAME_LENGTH]; +}; +struct fwupd_control_res { + u8 completion_code; + u8 curr_state; +}; +struct fwupd_get_update_status_res { + u8 completion_code; + u8 processing_status; + u8 percent_completion; + u8 check_status; + u8 error_code; +}; +struct fwupd_image_write_res { + u8 completion_code; + u8 no_of_bytes_written; +}; + +u16 fwupd_get_execution_ctx(u8 *req, u16 req_len, u8 *res); +u16 fwupd_get_rand_number(u8 *req, u16 req_len, u8 *res); +u16 fwupd_enter_update_mode(u8 *req, u16 req_len, u8 *res); +u16 fwupd_exit_update_mode(u8 *req, u16 req_len, u8 *res); +u16 fwupd_set_options(u8 *req, u16 req_len, u8 *res); +u16 fwupd_set_get_control(u8 *req, u16 req_len, u8 *res); +u16 fwupd_get_update_status(u8 *req, u16 req_len, u8 *res); +u16 fwupd_image_write(u8 *req, u16 req_len, u8 *res); diff --git a/board/aspeed/ast-g5/ipmi-handler.c b/board/aspeed/ast-g5/ipmi-handler.c index 9cccee9..5e78546 100644 --- a/board/aspeed/ast-g5/ipmi-handler.c +++ b/board/aspeed/ast-g5/ipmi-handler.c @@ -1,18 +1,37 @@ - // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2018-2019 Intel Corporation -#include "ipmi-handler.h" +#include "ipmi-fwupd.h" /* IPMI network function codes */ #define NETFN_APP 0x06 +#define NETFN_FIRMWARE 0x08 +#define NETFN_INTEL_OEM 0x30 /* IPMI command codes */ -#define CMD_GET_DEV_ID 0x01 -#define CMD_GET_SELF_TEST_RESULTS 0x04 +#define CMD_APP_GET_DEV_ID 0x01 +#define CMD_APP_GET_SELF_TEST_RESULTS 0x04 +#define CMD_FWUPD_GET_EXECUTION_CTX 0x23 +#define CMD_FWUPD_GET_RANDOM_NUMBER 0x26 +#define CMD_FWUPD_SET_UPDATE_MODE 0x27 +#define CMD_FWUPD_EXIT_UPDATE_MODE 0x28 +#define CMD_FWUPD_CONTROL_GET_SET 0x29 +#define CMD_FWUPD_GET_UPDATE_STATUS 0x2A +#define CMD_FWUPD_SET_OPTIONS 0x2B +#define CMD_FWUPD_IMAGE_WRITE 0x2C +#define CMD_INTL_OEM_GET_BUFFER_SIZE 0x66 + +#define MAX_KCS_BUF_SIZE 1020 /* (0xFF * 4) */ +#define MAX_IPMB_BUF_SIZE 1020 /* (0xFF * 4) */ typedef u16 (*fun_handler)(u8 *req, u16 req_len, u8 *res); +struct ipmi_cmd_table { + u8 net_fun; + u8 cmd; + fun_handler process_cmd; +}; + struct get_dev_id { u8 completion_code; u8 dev_id; @@ -29,11 +48,10 @@ struct self_test_res { u8 completion_code; u8 res_byte[2]; }; - -struct ipmi_cmd_table { - u8 net_fun; - u8 cmd; - fun_handler process_cmd; +struct intc_get_buf_size_res { + u8 completion_code; + u8 kcs_size; + u8 ipmb_size; }; static u16 get_device_id(u8 *req, u16 req_len, u8 *res) @@ -84,10 +102,36 @@ static u16 get_self_test_result(u8 *req, u16 req_len, u8 *res) return sizeof(struct self_test_res); } +static u16 intel_get_buffer_size(u8 *req, u16 req_len, u8 *res) +{ + struct intc_get_buf_size_res *result = + (struct intc_get_buf_size_res *)res; + + if (req_len != 0) { + result->completion_code = IPMI_CC_INVALID_DATA_LENGTH; + return sizeof(result->completion_code); + } + + /* Size is multiples of four bytes */ + result->completion_code = IPMI_CC_OK; + result->kcs_size = MAX_KCS_BUF_SIZE / 4; + result->ipmb_size = MAX_IPMB_BUF_SIZE / 4; + + return sizeof(struct intc_get_buf_size_res); +} const struct ipmi_cmd_table cmd_info[] = { - { NETFN_APP, CMD_GET_DEV_ID, get_device_id }, - { NETFN_APP, CMD_GET_SELF_TEST_RESULTS, get_self_test_result } + { NETFN_APP, CMD_APP_GET_DEV_ID, get_device_id }, + { NETFN_APP, CMD_APP_GET_SELF_TEST_RESULTS, get_self_test_result }, + { NETFN_FIRMWARE, CMD_FWUPD_GET_EXECUTION_CTX, fwupd_get_execution_ctx }, + { NETFN_FIRMWARE, CMD_FWUPD_GET_RANDOM_NUMBER, fwupd_get_rand_number }, + { NETFN_FIRMWARE, CMD_FWUPD_SET_UPDATE_MODE, fwupd_enter_update_mode }, + { NETFN_FIRMWARE, CMD_FWUPD_EXIT_UPDATE_MODE, fwupd_exit_update_mode }, + { NETFN_FIRMWARE, CMD_FWUPD_CONTROL_GET_SET, fwupd_set_get_control }, + { NETFN_FIRMWARE, CMD_FWUPD_GET_UPDATE_STATUS, fwupd_get_update_status }, + { NETFN_FIRMWARE, CMD_FWUPD_SET_OPTIONS, fwupd_set_options }, + { NETFN_FIRMWARE, CMD_FWUPD_IMAGE_WRITE, fwupd_image_write }, + { NETFN_INTEL_OEM, CMD_INTL_OEM_GET_BUFFER_SIZE, intel_get_buffer_size } }; #define CMD_TABLE_SIZE ARRAY_SIZE(cmd_info) diff --git a/board/aspeed/ast-g5/ipmi-handler.h b/board/aspeed/ast-g5/ipmi-handler.h index 9d46d9b..8eea930 100644 --- a/board/aspeed/ast-g5/ipmi-handler.h +++ b/board/aspeed/ast-g5/ipmi-handler.h @@ -1,4 +1,3 @@ - /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2018-2019 Intel Corporation */ @@ -6,12 +5,14 @@ /* IPMI completion codes */ #define IPMI_CC_OK 0x00 +#define IPMI_CC_INVALID_CODE 0x80 #define IPMI_CC_NODE_BUSY 0xC0 #define IPMI_CC_INVALID_CMD 0xC1 #define IPMI_CC_INVALID_CMD_LUN 0xC2 #define IPMI_CC_OUT_OF_SPACE 0xC4 #define IPMI_CC_INVALID_DATA_LENGTH 0xC7 #define IPMI_CC_INVALID_DATA_FIELD 0xCC +#define IPMI_CC_NOT_SUPPORTED_IN_STATE 0xD5 #define IPMI_CC_UNSPECIFIED 0xFF /* BMC IPMB LUNs */ diff --git a/common/autoboot.c b/common/autoboot.c index d66c0fa..45a600e 100644 --- a/common/autoboot.c +++ b/common/autoboot.c @@ -349,6 +349,17 @@ void autoboot_command(const char *s) { debug("### main_loop: bootcmd=\"%s\"\n", s ? s : ""); +#ifdef AST_G5_INTEL + /* TODO: Make run_command_list as non-blocking(blocked by getc()) + * and make main u-boot loop to check both keyboard inputs as well + * as start_update firmware flags during FFUJ. + * This will make sure debug mode intact during FFUJ. + */ + if (intel_force_firmware_jumper_enabled()) { + start_fw_update_loop(); + } +#endif + if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) { #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) int prev = disable_ctrlc(1); /* disable Control C checking */ diff --git a/configs/ast_g5_phy_defconfig b/configs/ast_g5_phy_defconfig index 1b96ab7..5965a9b 100644 --- a/configs/ast_g5_phy_defconfig +++ b/configs/ast_g5_phy_defconfig @@ -15,3 +15,4 @@ CONFIG_SYS_NS16550=y CONFIG_USE_IRQ=y CONFIG_CMD_I2C=y CONFIG_SYS_I2C_AST=y +CONFIG_LIB_RAND=y