diff --git a/kestrel/src/kestrel.c b/kestrel/src/kestrel.c index e996dd0141441962c1db91f221c19a2ca3965d72..a2436f698c3dfd1df4cbafa8ee1e943ec81b2a72 100644 --- a/kestrel/src/kestrel.c +++ b/kestrel/src/kestrel.c @@ -21,6 +21,9 @@ LOG_MODULE_REGISTER(kestrel_core, LOG_LEVEL_DBG); #include #include #include +#if (WITH_ZEPHYR) +#include +#endif #if (WITH_SPI) #include "micron_n25q_flash.h" #include "tercel_spi.h" @@ -52,6 +55,9 @@ LOG_MODULE_REGISTER(kestrel_core, LOG_LEVEL_DBG); // Host platform configuration #define HOST_PLATFORM_FPGA_I2C_ADDRESS 0x31 +// Limits +#define FLASH_WRITE_LOCATION_RETRY_LIMIT 5 + extern uint32_t irq_unhandled_vector; extern uint32_t irq_unhandled_source; extern uint8_t irq_unhandled_vector_valid; @@ -121,6 +127,9 @@ uint8_t post_code_low = 0; static uint8_t allow_flash_write = 0; static uint8_t enable_post_code_console_output = 0; +// External service interface +struct firmware_buffer_region main_firmware_buffer; + // System status uint8_t kestrel_basic_init_complete = 0; @@ -138,6 +147,15 @@ const struct shell *host_console_service_task_shell = NULL; #define KESTREL_LOG(...) LOG_INF(__VA_ARGS__) +#if (WITH_ZEPHYR) +#if (DEBUG_HOST_SPI_FLASH_READ) +static uint32_t crc32(const uint8_t *data, size_t len) +{ + return crc32_ieee(data, len); +} +#endif +#endif + typedef struct { int8_t index; @@ -894,14 +912,93 @@ static void hiomap_remove_write_window(uint32_t start_address) } } +#if (WITH_SPI) +static uint32_t read_host_spi_flash_id(uintptr_t spi_ctl_cfgaddr, uintptr_t spi_ctl_baseaddr) +{ + uint32_t flash_id = 0; + + // Set user mode + write_tercel_register(spi_ctl_cfgaddr, TERCEL_SPI_REG_SYS_CORE_CTL1, + read_tercel_register(spi_ctl_cfgaddr, TERCEL_SPI_REG_SYS_CORE_CTL1) | + (TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + // Send Flash ID command + *((volatile uint8_t *)spi_ctl_baseaddr) = 0x9e; + + // Read response + flash_id = (flash_id << 8) | (*((volatile uint8_t *)spi_ctl_baseaddr) & 0xff); + flash_id = (flash_id << 8) | (*((volatile uint8_t *)spi_ctl_baseaddr) & 0xff); + flash_id = (flash_id << 8) | (*((volatile uint8_t *)spi_ctl_baseaddr) & 0xff); + flash_id = (flash_id << 8) | (*((volatile uint8_t *)spi_ctl_baseaddr) & 0xff); + + // Clear user mode + write_tercel_register(spi_ctl_cfgaddr, TERCEL_SPI_REG_SYS_CORE_CTL1, + read_tercel_register(spi_ctl_cfgaddr, TERCEL_SPI_REG_SYS_CORE_CTL1) & + ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); + + return flash_id; +} + static int write_data_to_flash(uintptr_t spi_ctl_cfgaddr, uintptr_t spi_ctl_baseaddr, uint8_t *data, uint32_t bytes, uint32_t flash_offset, uint8_t erase_before_write) { + uint32_t flash_device_id; uint32_t flash_address; uint32_t bytes_remaining; + uint8_t flag_status_register; + uint8_t retry_operation; + uint8_t retry_count; + size_t max_bytes_per_write; + int micron_n25q_device_found; + int write_is_32bits; + int i; + int retcode; + + // Check alignment + const uintptr_t alignment_mask = sizeof(mem_word_t) - 1; + if ((uintptr_t)data & alignment_mask) + { + KESTREL_LOG("\n[ERROR] Unaligned data passed to write_data_to_flash()\n"); + return -2; + } + write_is_32bits = 1; + if (bytes & 0x3) + { + write_is_32bits = 0; + } // Limit Flash address to active memory region of SPI Flash controller flash_offset = flash_offset & 0x0fffffff; + // Read device ID in case special handling is required + flash_device_id = read_host_spi_flash_id(spi_ctl_cfgaddr, spi_ctl_baseaddr); + micron_n25q_device_found = 0; + for (i = 0; i < (sizeof(micron_n25q_spi_device_ids) / sizeof(micron_n25q_spi_device_ids[0])); i++) + { + if (flash_device_id == micron_n25q_spi_device_ids[i]) + { + micron_n25q_device_found = 1; + } + } + if (micron_n25q_device_found) + { + // The N25Q512A and related devices are "special" + // For some reason, it requires a flag status register read after every PAGE PROGRAM command + // This means we can only write up to four bytes at a time in 32-bit copy mode + if (write_is_32bits) + { + max_bytes_per_write = 4; + } + else + { + max_bytes_per_write = 1; + } + } + else + { + max_bytes_per_write = FLASH_PAGE_SIZE_BYTES; + } + + retcode = 0; if (allow_flash_write) { // Flash erase if needed, then write data @@ -916,26 +1013,67 @@ static int write_data_to_flash(uintptr_t spi_ctl_cfgaddr, uintptr_t spi_ctl_base configure_flash_write_enable(spi_ctl_cfgaddr, spi_ctl_baseaddr, 0); } - for (flash_address = flash_offset; (flash_address - flash_offset) < bytes; flash_address = flash_address + FLASH_PAGE_SIZE_BYTES) + retry_operation = 0; + retry_count = 0; + for (flash_address = flash_offset; (flash_address - flash_offset) < bytes; flash_address = flash_address + max_bytes_per_write) { + if (retry_operation) + { + flash_address = flash_address - max_bytes_per_write; + retry_operation = 0; + retry_count++; + } bytes_remaining = bytes - (flash_address - flash_offset); configure_flash_write_enable(spi_ctl_cfgaddr, spi_ctl_baseaddr, 1); while (!(read_flash_flag_status_register(spi_ctl_cfgaddr, spi_ctl_baseaddr) & 0x80)) { // Wait for pending operation to complete } - memcpy((uint8_t *)(spi_ctl_baseaddr + flash_address), data + (flash_address - flash_offset), - (bytes_remaining > FLASH_PAGE_SIZE_BYTES) ? FLASH_PAGE_SIZE_BYTES : bytes_remaining); - while (!(read_flash_flag_status_register(spi_ctl_cfgaddr, spi_ctl_baseaddr) & 0x80)) + if (write_is_32bits) + { + memcpy32((uint32_t *)(spi_ctl_baseaddr + flash_address), (uint32_t *)(data + (flash_address - flash_offset)), + (bytes_remaining > max_bytes_per_write) ? (max_bytes_per_write / 4) : (bytes_remaining / 4)); + } + else + { + memcpy((uint8_t *)(spi_ctl_baseaddr + flash_address), data + (flash_address - flash_offset), + (bytes_remaining > max_bytes_per_write) ? max_bytes_per_write : bytes_remaining); + } + while (!((flag_status_register = read_flash_flag_status_register(spi_ctl_cfgaddr, spi_ctl_baseaddr)) & 0x80)) { // Wait for pending operation to complete } + if (flag_status_register & 0x10) + { + retry_operation = 1; + } + else { + if (memcmp((uint8_t *)(spi_ctl_baseaddr + flash_address), data + (flash_address - flash_offset), + (bytes_remaining > max_bytes_per_write) ? max_bytes_per_write : bytes_remaining) != 0) + { + retry_operation = 1; + } + } + if (retry_operation && (retry_count > FLASH_WRITE_LOCATION_RETRY_LIMIT)) + { + KESTREL_LOG("\n[WARNING] Program failed at address 0x%08x (flag status 0x%02x)! Retry limit reached, aborting write to location.\n", flash_address, flag_status_register); + retry_operation = 0; + retry_count = 0; + retcode = -1; + } +#if (WITH_ZEPHYR) + if (retry_operation) + { + // Give the Flash device some time to recover / flush data + k_usleep(100000); + } +#endif } configure_flash_write_enable(spi_ctl_cfgaddr, spi_ctl_baseaddr, 0); } - return 0; + return retcode; } static int write_hiomap_data_to_flash(uintptr_t spi_ctl_cfgaddr, uintptr_t spi_ctl_baseaddr, uint32_t bytes, uint32_t flash_offset, uint8_t erase_before_write) @@ -974,10 +1112,9 @@ static int write_hiomap_data_to_flash(uintptr_t spi_ctl_cfgaddr, uintptr_t spi_c retcode = write_data_to_flash(spi_ctl_cfgaddr, spi_ctl_baseaddr, write_buffer, bytes, flash_offset, erase_before_write); - hiomap_remove_write_window(start_address); - return retcode; } +#endif // NOTE // The POWER9 host uses true multitasking (kernel preemptive), so it is entirely @@ -1182,6 +1319,9 @@ static int process_host_to_bmc_ipmi_bt_transactions(void) } hiomap_config.dirty_range_count = 0; + // Close active window + hiomap_remove_write_window(hiomap_config.window_start_address); + response.data[0] = ipmi_bt_current_request.data[0]; response.data[1] = ipmi_bt_current_request.data[1]; response.length = BASE_HIOMAP_RESPONSE_LENGTH; @@ -1189,6 +1329,23 @@ static int process_host_to_bmc_ipmi_bt_transactions(void) break; case HIOMAP_CMD_CREATE_RD_WIN: case HIOMAP_CMD_CREATE_WR_WIN: + // Implicitly shut down any active window + if (hiomap_config.window_type == HIOMAP_WINDOW_TYPE_WRITE) + { + // Implicitly flush any dirty ranges + for (i = 0; i < hiomap_config.dirty_range_count; i++) + { + write_hiomap_data_to_flash(HOSTSPIFLASHCFG_BASE, HOSTSPIFLASH_BASE, + hiomap_config.dirty_ranges[i].bytes, hiomap_config.dirty_ranges[i].start_address, + !hiomap_config.dirty_ranges[i].erased); + } + hiomap_config.dirty_range_count = 0; + + // Close active window + hiomap_remove_write_window(hiomap_config.window_start_address); + } + hiomap_config.window_type = HIOMAP_WINDOW_INACTIVE; + // Parse request data hiomap_config.window_start_address = (((((uint32_t)ipmi_bt_current_request.data[3]) << 8) | ipmi_bt_current_request.data[2]) << FLASH_BLOCK_SIZE_SHIFT) & @@ -1270,15 +1427,6 @@ static int process_host_to_bmc_ipmi_bt_transactions(void) if (hiomap_use_direct_access) { - // Implicitly flush any dirty ranges - for (i = 0; i < hiomap_config.dirty_range_count; i++) - { - write_hiomap_data_to_flash(HOSTSPIFLASHCFG_BASE, HOSTSPIFLASH_BASE, - hiomap_config.dirty_ranges[i].bytes, hiomap_config.dirty_ranges[i].start_address, - !hiomap_config.dirty_ranges[i].erased); - } - hiomap_config.dirty_range_count = 0; - // Locate or allocate Flash sector buffer as required hiomap_write_buffer = NULL; for (i = 0; i < hiomap_config.write_window_count; i++) @@ -2982,32 +3130,6 @@ static void console_service(void) #endif #if (WITH_SPI) -static uint32_t read_host_spi_flash_id(uintptr_t spi_ctl_cfgaddr, uintptr_t spi_ctl_baseaddr) -{ - uint32_t flash_id = 0; - - // Set user mode - write_tercel_register(spi_ctl_cfgaddr, TERCEL_SPI_REG_SYS_CORE_CTL1, - read_tercel_register(spi_ctl_cfgaddr, TERCEL_SPI_REG_SYS_CORE_CTL1) | - (TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); - - // Send Flash ID command - *((volatile uint8_t *)spi_ctl_baseaddr) = 0x9e; - - // Read response - flash_id = (flash_id << 8) | (*((volatile uint8_t *)spi_ctl_baseaddr) & 0xff); - flash_id = (flash_id << 8) | (*((volatile uint8_t *)spi_ctl_baseaddr) & 0xff); - flash_id = (flash_id << 8) | (*((volatile uint8_t *)spi_ctl_baseaddr) & 0xff); - flash_id = (flash_id << 8) | (*((volatile uint8_t *)spi_ctl_baseaddr) & 0xff); - - // Clear user mode - write_tercel_register(spi_ctl_cfgaddr, TERCEL_SPI_REG_SYS_CORE_CTL1, - read_tercel_register(spi_ctl_cfgaddr, TERCEL_SPI_REG_SYS_CORE_CTL1) & - ~(TERCEL_SPI_ENABLE_USER_MODE_MASK << TERCEL_SPI_ENABLE_USER_MODE_SHIFT)); - - return flash_id; -} - static int tercel_spi_flash_init(uintptr_t spi_ctl_cfgaddr, uintptr_t spi_ctl_baseaddr) { int i; @@ -3141,6 +3263,7 @@ static int tercel_spi_flash_init(uintptr_t spi_ctl_cfgaddr, uintptr_t spi_ctl_ba #if (WITH_SPI) #define SPI_READ_TRANSFER_SIZE (1 * 1024 * 1024LL) +#define SPI_WRITE_TRANSFER_SIZE (1 * 1024 * 1024LL) int copy_spi_flash_to_internal_buffer(uintptr_t flash_data, uintptr_t flash_ctl, uint8_t *buffer) { int retcode; @@ -3225,6 +3348,54 @@ int copy_spi_flash_to_internal_buffer(uintptr_t flash_data, uintptr_t flash_ctl, } #endif +int write_flash_buffer_to_flash(void) +{ + int retcode; + int chunk; + int allow_flash_write_prev; + + if (main_firmware_buffer.locked) + { + return -2; + } + + KESTREL_LOG("Writing host Flash ROM from internal buffer (%p)...", (void*)main_firmware_buffer.buffer_address); + retcode = 0; + + allow_flash_write_prev = allow_flash_write; + allow_flash_write = 1; + + for (chunk = 0; chunk < FLASH_SIZE_BYTES / SPI_WRITE_TRANSFER_SIZE; chunk++) + { + if (write_data_to_flash(HOSTSPIFLASHCFG_BASE, HOSTSPIFLASH_BASE, main_firmware_buffer.buffer_address + (chunk * SPI_WRITE_TRANSFER_SIZE), SPI_WRITE_TRANSFER_SIZE, chunk * SPI_WRITE_TRANSFER_SIZE, 1)) + { + // Fast abort on fatal error + KESTREL_LOG("\rFAILED!\n"); + retcode = -1; + break; + } + KESTREL_LOG("\r[%d/%lld]", chunk + 1, FLASH_SIZE_BYTES / SPI_WRITE_TRANSFER_SIZE); + } + + allow_flash_write = allow_flash_write_prev; + + // Verify write + KESTREL_LOG("Verifying host Flash ROM..."); + for (chunk = 0; chunk < FLASH_SIZE_BYTES / SPI_READ_TRANSFER_SIZE; chunk++) + { + if (memcmp((uint8_t*)((uintptr_t)HOSTSPIFLASH_BASE + (chunk * SPI_READ_TRANSFER_SIZE)), main_firmware_buffer.buffer_address + (chunk * SPI_READ_TRANSFER_SIZE), SPI_READ_TRANSFER_SIZE) != 0) + { + // Fast abort on fatal error + KESTREL_LOG("\rFAILED!\n"); + retcode = -1; + break; + } + KESTREL_LOG("\r[%d/%lld]", chunk + 1, FLASH_SIZE_BYTES / SPI_READ_TRANSFER_SIZE); + } + + return retcode; +} + int kestrel_init(void) { uint32_t dword; @@ -3298,11 +3469,18 @@ int kestrel_init(void) #endif } - host_flash_buffer = malloc((64 + 2) * 1024LL * 1024LL); - if (!host_flash_buffer) { + main_firmware_buffer.locked = 1; + main_firmware_buffer.buffer_length = (64 + 2) * 1024LL * 1024LL; +#if (WITH_ZEPHYR) + main_firmware_buffer.buffer_address = malloc(main_firmware_buffer.buffer_length); +#else + main_firmware_buffer.buffer_address = (uint8_t *)(MAIN_RAM_BASE + 0x3e00000); +#endif + if (!main_firmware_buffer.buffer_address) { KESTREL_LOG("[ERROR] Unable to allocate host Flash ROM buffer, ABORTING"); return -1; } + host_flash_buffer = main_firmware_buffer.buffer_address; if (CLEAR_FLASH_CACHE_BUFFER_ON_STARTUP) { // Clear SPI Flash buffer @@ -3310,6 +3488,7 @@ int kestrel_init(void) memset32((uint32_t *)host_flash_buffer, 0xdeadbeef, (0x4000000 + 0x200000) / 4); KESTREL_LOG("\rInternal host ROM buffer cleared "); } + main_firmware_buffer.locked = 0; display_character('1', 0); // STATUS CODE: 1 @@ -3339,39 +3518,39 @@ int kestrel_init(void) } #if (DEBUG_HOST_SPI_FLASH_READ) - KESTREL_LOG("host_flash_buffer: %p First 1KB:", host_flash_buffer); + printk("host_flash_buffer: %p First 1KB:\n", host_flash_buffer); int byte = 0; int row = 0; for (row = 0; row < 32; row++) { for (byte = 0; byte < 32; byte++) { - KESTREL_LOG("%02x ", host_flash_buffer[byte + (row * 32)]); + printk("%02x ", host_flash_buffer[byte + (row * 32)]); } - KESTREL_LOG("\n"); + printk("\n"); } - KESTREL_LOG("[1/5] CRC of first 64B: %08x", crc32(host_flash_buffer, 1 * 64LL)); - KESTREL_LOG("[2/5] CRC of first 1KB: %08x (next %08x, next %08x)", crc32(host_flash_buffer, 1024LL), crc32(host_flash_buffer + 1024LL, 1024LL), + printk("[1/5] CRC of first 64B: %08x\n", crc32(host_flash_buffer, 1 * 64LL)); + printk("[2/5] CRC of first 1KB: %08x (next %08x, next %08x)\n", crc32(host_flash_buffer, 1024LL), crc32(host_flash_buffer + 1024LL, 1024LL), crc32(host_flash_buffer + (2 * 1024LL), 1024LL)); - KESTREL_LOG("[3/5] CRC of first 1KB: %08x (next %08x, next %08x)", crc32(host_flash_buffer, 1024LL), crc32(host_flash_buffer + 1024LL, 1024LL), + printk("[3/5] CRC of first 1KB: %08x (next %08x, next %08x)\n", crc32(host_flash_buffer, 1024LL), crc32(host_flash_buffer + 1024LL, 1024LL), crc32(host_flash_buffer + (2 * 1024LL), 1024LL)); - KESTREL_LOG("[4/5] CRC of first 1MB: %08x", crc32(host_flash_buffer, 1 * 1024 * 1024LL)); - KESTREL_LOG("[5/5] CRC of full 64MB: %08x", crc32(host_flash_buffer, 64 * 1024 * 1024LL)); + printk("[4/5] CRC of first 1MB: %08x\n", crc32(host_flash_buffer, 1 * 1024 * 1024LL)); + printk("[5/5] CRC of full 64MB: %08x\n", crc32(host_flash_buffer, 64 * 1024 * 1024LL)); // HBBL on test sytem is from 0x206200 to 0x207388 inclusive - KESTREL_LOG("\nHBBL region:\n"); + printk("\nHBBL region:\n"); for (row = 0; row < 141; row++) { for (byte = 0; byte < 32; byte++) { - KESTREL_LOG("%02x ", *(host_flash_buffer + 0x206200ULL + (byte + (row * 32)))); - // KESTREL_LOG("%02x ", *(host_flash_buffer + 0x1000ULL + (byte + (row * + printk("%02x ", *(host_flash_buffer + 0x206200ULL + (byte + (row * 32)))); + // printk("%02x ", *(host_flash_buffer + 0x1000ULL + (byte + (row * // 32)))); } - KESTREL_LOG("\n"); + printk("\n"); } - KESTREL_LOG("CRC of HBBL region: %08x\n", crc32(host_flash_buffer + 0x206200ULL, 4488)); + printk("CRC of HBBL region: %08x\n", crc32(host_flash_buffer + 0x206200ULL, 4488)); #endif #endif diff --git a/kestrel/src/kestrel.h b/kestrel/src/kestrel.h index d26c13b48366c34e662401a1d31f92b8dc37d5c1..0aa57bf1bdad122922dbadfadcbe6a89e713b41b 100644 --- a/kestrel/src/kestrel.h +++ b/kestrel/src/kestrel.h @@ -11,6 +11,16 @@ #define KESTREL_SERVICE_THREAD_PRIORITY K_PRIO_COOP(1) #endif +struct firmware_buffer_region { + uint8_t *buffer_address; + unsigned long long buffer_length; + uintptr_t current_write_offset; + uint8_t locked; + uint8_t overflow; +}; + +extern struct firmware_buffer_region main_firmware_buffer; + extern uint8_t kestrel_basic_init_complete; extern uint8_t host_background_service_task_active; extern uint8_t host_console_service_task_requested; @@ -25,6 +35,7 @@ int kestrel_init(void); int power_on_host(void); void power_off_chassis(void); void print_chassis_status(void); +int write_flash_buffer_to_flash(void); int primary_service_event_loop(void); diff --git a/kestrel/src/shell.c b/kestrel/src/shell.c index b60ef4f49f4573ce09f90ee7af4eb66832e83e66..39d38bfc000cf2cee1afe2529a1d848efd67f25f 100644 --- a/kestrel/src/shell.c +++ b/kestrel/src/shell.c @@ -31,14 +31,14 @@ static void kestrel_console_thread(const void *p1, const void *p2) void *active_log_backend = (void*)p2; uint32_t active_log_level = CONFIG_LOG_DEFAULT_LEVEL; - // Get shell printf context - const struct shell_fprintf *sh_fprintf = shell->fprintf_ctx; + // Get shell printf context + const struct shell_fprintf *sh_fprintf = shell->fprintf_ctx; k_mutex_lock(&shell->ctx->wr_mtx, K_FOREVER); shell_stop(shell); - sh_fprintf->fwrite((const struct shell *)sh_fprintf->user_ctx, CONSOLE_ATTACH_BANNER, strlen(CONSOLE_ATTACH_BANNER)); + sh_fprintf->fwrite((const struct shell *)sh_fprintf->user_ctx, CONSOLE_ATTACH_BANNER, strlen(CONSOLE_ATTACH_BANNER)); attach_to_host_console(shell); @@ -93,6 +93,14 @@ static void print_kestrel_utility_command_usage(const struct shell *shell, char* shell_print(shell, "Usage: %s ", command_name); } +static void print_kestrel_reflash_command_usage(const struct shell *shell, char* command_name) { + shell_print(shell, "Usage: %s ", command_name); +} + +static void print_kestrel_memdump_command_usage(const struct shell *shell, char* command_name) { + shell_print(shell, "Usage: %s
[length]", command_name); +} + static int kestrel_shell_cmd_utility(const struct shell *shell, size_t argc, char **argv) { if (argc < 2) { @@ -118,6 +126,56 @@ static int kestrel_shell_cmd_utility(const struct shell *shell, size_t argc, cha } } +static int kestrel_shell_cmd_reflash(const struct shell *shell, size_t argc, char **argv) +{ + if (argc < 2) { + print_kestrel_reflash_command_usage(shell, argv[0]); + return -1; + } + + if (strcmp(argv[1], "pnor") == 0) { + VERIFY_KESTREL_START_COMPLETE(shell) + + return write_flash_buffer_to_flash(); + } + else { + shell_print(shell, "%s: Invalid parameter", argv[0]); + print_kestrel_reflash_command_usage(shell, argv[0]); + return -1; + } +} + +static int kestrel_shell_cmd_memdump(const struct shell *shell, size_t argc, char **argv) +{ + uintptr_t address; + size_t length; + int pos; + int remaining; + + if (argc < 2) { + print_kestrel_memdump_command_usage(shell, argv[0]); + return -1; + } + + address = strtoul(argv[1], NULL, 16); + if (argc > 2) { + length = strtoul(argv[2], NULL, 16); + } + else { + length = 1; + } + + for (pos = 0; pos < length; pos += remaining) { + remaining = MIN(length - pos, SHELL_HEXDUMP_BYTES_IN_LINE); + shell_hexdump_line(shell, address, (uint8_t*)address, remaining); + address += remaining; + } + + shell_print(shell, ""); + + return 0; +} + static int kestrel_shell_cmd_console(const struct shell *shell, size_t argc, char **argv) { const void *active_log_backend = NULL; @@ -153,6 +211,10 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_kestrel, SHELL_CMD(obmcutil, NULL, "OBMC-compatible utilty command", kestrel_shell_cmd_utility), // OpenBMC console command (compat) SHELL_CMD(obmc-console-client, NULL, "OBMC-compatible console command", kestrel_shell_cmd_console), + // Flash programming command + SHELL_CMD(reflash, NULL, "Simple Flash utility", kestrel_shell_cmd_reflash), + // Arbitrary memory dump command + SHELL_CMD(memdump, NULL, "Memory dump tool", kestrel_shell_cmd_memdump), // Command list array terminator SHELL_SUBCMD_SET_END ); diff --git a/kestrel/src/webservice.c b/kestrel/src/webservice.c index c15b522065fa35bce4afeac2cff82d13adcd0937..c6fe73f2c3d2045a53b6165080e49640dda63f0c 100644 --- a/kestrel/src/webservice.c +++ b/kestrel/src/webservice.c @@ -10,6 +10,9 @@ #include "civetweb.h" +#define WITH_ZEPHYR 1 +#include "kestrel.h" + #define HTTP_PORT 8080 #define HTTPS_PORT 4443 @@ -54,6 +57,7 @@ int hello_world_handler(struct mg_connection *conn, void *cbdata) mg_printf(conn, "\n"); mg_printf(conn, "\n"); @@ -141,6 +145,98 @@ int history_handler(struct mg_connection *conn, void *cbdata) return 200; } +int firmware_upload_form_handler(struct mg_connection *conn, void *cbdata) +{ + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: " + "close\r\n\r\n"); + + mg_printf(conn, "\n"); + mg_printf(conn, "\n\n"); + mg_printf(conn, "\n"); + mg_printf(conn, "File upload\n"); + mg_printf(conn, "\n\n"); + mg_printf(conn, + "
\n", + (const char *)cbdata); + mg_printf(conn, "\n"); + mg_printf(conn, "\n"); + mg_printf(conn, "
\n\n\n"); + + return 200; +} + +int fw_field_received(const char *key, const char *filename, char *path, size_t pathlen, void *user_data) +{ + (void)key; + (void)path; + (void)pathlen; + + if (strcmp(key, "firmwarefile") == 0) { + return MG_FORM_FIELD_STORAGE_GET; + } + + return MG_FORM_FIELD_STORAGE_ABORT; +} + + +int fw_data_received(const char *key, const char *value, size_t valuelen, void *user_data) +{ + struct firmware_buffer_region *fw_data = (struct firmware_buffer_region *)user_data; + + if ((fw_data->current_write_offset + valuelen) > fw_data->buffer_length) { + // Buffer overflow! + // Discard the extra data... + valuelen = fw_data->buffer_length - fw_data->current_write_offset; + fw_data->overflow = 1; + } + memcpy(fw_data->buffer_address + fw_data->current_write_offset, value, valuelen); + fw_data->current_write_offset += valuelen; + + return 0; +} + +int firmware_upload_handler(struct mg_connection *conn, void *cbdata) +{ + /* Handler may access the request info using mg_get_request_info */ + const struct mg_request_info *req_info = mg_get_request_info(conn); + int ret; + struct mg_form_data_handler fdh = {fw_field_received, + fw_data_received, + 0, + (void *)&main_firmware_buffer}; + + /* It would be possible to check the request info here before calling + * mg_handle_form_request. */ + (void)req_info; + + mg_printf(conn, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n"); + + main_firmware_buffer.locked = 1; + main_firmware_buffer.overflow = 0; + main_firmware_buffer.current_write_offset = 0; + + /* Call the form handler */ + mg_printf(conn, "File(s) uploaded:"); + ret = mg_handle_form_request(conn, &fdh); + + main_firmware_buffer.locked = 0; + + mg_printf(conn, "\r\n%i files\r\n", ret); + mg_printf(conn, "\r\n%ld bytes\r\n", main_firmware_buffer.current_write_offset); + + if (main_firmware_buffer.overflow) { + mg_printf(conn, "\r\nWARNING: Data was discarded due to buffer overflow!r\n"); + return 500; + } + + return 200; +} + void *main_pthread(void *arg) { static const char * const options[] = { @@ -166,9 +262,12 @@ void *main_pthread(void *arg) return 0; } - mg_set_request_handler(ctx, "/$", hello_world_handler, 0); - mg_set_request_handler(ctx, "/info$", system_info_handler, 0); - mg_set_request_handler(ctx, "/history", history_handler, 0); + mg_set_request_handler(ctx, "/$", hello_world_handler, (void *)0); + mg_set_request_handler(ctx, "/info$", system_info_handler, (void *)0); + mg_set_request_handler(ctx, "/history", history_handler, (void *)0); + + mg_set_request_handler(ctx, "/firmwareupload", firmware_upload_form_handler, (void *)"/firmwareupload.callback"); + mg_set_request_handler(ctx, "/firmwareupload.callback", firmware_upload_handler, (void *)0); return 0; } diff --git a/prj.conf b/prj.conf index 6f449075bc9627b23181896e93b6cd59d6761d3d..5ebaf633425547e10ff9870d432ab0c2de65f24a 100644 --- a/prj.conf +++ b/prj.conf @@ -12,6 +12,8 @@ CONFIG_DEVICE_SHELL=y CONFIG_POSIX_CLOCK=y CONFIG_DATE_SHELL=y CONFIG_NET_SHELL=y +CONFIG_FLASH_SHELL=y +CONFIG_FLASH_MAP_SHELL=y CONFIG_SHELL_CMDS_SELECT=y CONFIG_SHELL_BACKEND_SERIAL=y CONFIG_SHELL_BACKEND_TELNET=y @@ -28,6 +30,12 @@ CONFIG_FILE_SYSTEM_LITTLEFS=y # Kestrel specific configuration CONFIG_NET_TC_THREAD_PREEMPTIVE=y +CONFIG_NET_TC_TX_COUNT=1 + +# The Kestrel CPU is quite slow, and it's easy to starve the CivetWeb process +# during file uploads if the TCP queues have a higher preempt priority than +# the CivetWeb process (by default, this is true)... +CONFIG_NUM_PREEMPT_PRIORITIES=1 # Kernel options CONFIG_TEST_RANDOM_GENERATOR=y @@ -70,10 +78,10 @@ CONFIG_NET_MGMT=y CONFIG_NET_MGMT_EVENT=y # Network buffers -CONFIG_NET_PKT_RX_COUNT=96 -CONFIG_NET_PKT_TX_COUNT=96 -CONFIG_NET_BUF_RX_COUNT=128 -CONFIG_NET_BUF_TX_COUNT=128 +CONFIG_NET_PKT_RX_COUNT=4096 +CONFIG_NET_PKT_TX_COUNT=4096 +CONFIG_NET_BUF_RX_COUNT=4096 +CONFIG_NET_BUF_TX_COUNT=4096 CONFIG_NET_CONTEXT_NET_PKT_POOL=y CONFIG_NET_BUF_POOL_USAGE=y