Add initial (primitive) firmware upload / Flash write support

parent 9b539060
......@@ -21,6 +21,9 @@ LOG_MODULE_REGISTER(kestrel_core, LOG_LEVEL_DBG);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if (WITH_ZEPHYR)
#include <sys/types.h>
#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
......
......@@ -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);
......
......@@ -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 <poweron chassison chassisoff>", command_name);
}
static void print_kestrel_reflash_command_usage(const struct shell *shell, char* command_name) {
shell_print(shell, "Usage: %s <pnor>", command_name);
}
static void print_kestrel_memdump_command_usage(const struct shell *shell, char* command_name) {
shell_print(shell, "Usage: %s <address> [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
);
......
......@@ -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, "<ul>\n");
mg_printf(conn, "<li><a href=/info>system info</a></li>\n");
mg_printf(conn, "<li><a href=/history>cookie demo</a></li>\n");
mg_printf(conn, "<li><a href=/firmwareupload>firmware upload</a></li>\n");
mg_printf(conn, "</ul>\n");
mg_printf(conn, "</body></html>\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, "<!DOCTYPE html>\n");
mg_printf(conn, "<html>\n<head>\n");
mg_printf(conn, "<meta charset=\"UTF-8\">\n");
mg_printf(conn, "<title>File upload</title>\n");
mg_printf(conn, "</head>\n<body>\n");
mg_printf(conn,
"<form action=\"%s\" method=\"POST\" "
"enctype=\"multipart/form-data\">\n",
(const char *)cbdata);
mg_printf(conn, "<input type=\"file\" name=\"firmwarefile\" multiple>\n");
mg_printf(conn, "<input type=\"submit\" value=\"Submit\">\n");
mg_printf(conn, "</form>\n</body>\n</html>\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;
}
......
......@@ -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