[app][lkboot] restructure to add booting out of flash
-Now lkboot will start a network server (if networking is enabled)
and wait for a predetermined amount of time (5 seconds currently).
If after that time no one has talked to it, it will try to boot
an image out of flash.
-If no networking, then directly boot an image out of flash immediately.
TODO: configure timeout, add 'autoboot' command to lkboot to let the host
short circuit the wait.
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
#include <endian.h>
|
||||
#include <malloc.h>
|
||||
#include <arch.h>
|
||||
#include <err.h>
|
||||
#include <trace.h>
|
||||
|
||||
#include <kernel/thread.h>
|
||||
@@ -48,178 +49,270 @@
|
||||
|
||||
#define bootdevice "spi0"
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
extern void *lkb_iobuffer;
|
||||
extern paddr_t lkb_iobuffer_phys;
|
||||
extern size_t lkb_iobuffer_size;
|
||||
|
||||
struct lkb_command {
|
||||
struct lkb_command *next;
|
||||
const char *name;
|
||||
lkb_handler_t handler;
|
||||
void *cookie;
|
||||
struct lkb_command *next;
|
||||
const char *name;
|
||||
lkb_handler_t handler;
|
||||
void *cookie;
|
||||
};
|
||||
|
||||
struct lkb_command *lkb_cmd_list = NULL;
|
||||
|
||||
void lkb_register(const char *name, lkb_handler_t handler, void *cookie) {
|
||||
struct lkb_command *cmd = malloc(sizeof(struct lkb_command));
|
||||
if (cmd != NULL) {
|
||||
cmd->next = lkb_cmd_list;
|
||||
cmd->name = name;
|
||||
cmd->handler = handler;
|
||||
cmd->cookie = cookie;
|
||||
lkb_cmd_list = cmd;
|
||||
}
|
||||
struct lkb_command *cmd = malloc(sizeof(struct lkb_command));
|
||||
if (cmd != NULL) {
|
||||
cmd->next = lkb_cmd_list;
|
||||
cmd->name = name;
|
||||
cmd->handler = handler;
|
||||
cmd->cookie = cookie;
|
||||
lkb_cmd_list = cmd;
|
||||
}
|
||||
}
|
||||
|
||||
static int do_reboot(void *arg) {
|
||||
thread_sleep(250);
|
||||
platform_halt(HALT_ACTION_REBOOT, HALT_REASON_SW_RESET);
|
||||
return 0;
|
||||
thread_sleep(250);
|
||||
platform_halt(HALT_ACTION_REBOOT, HALT_REASON_SW_RESET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_boot(void *arg) {
|
||||
thread_sleep(250);
|
||||
thread_sleep(250);
|
||||
|
||||
/* construct a boot argument list */
|
||||
// XXX get memory smarter than this
|
||||
const size_t bootargs_size = 64*1024;
|
||||
void *args = (void *)((uintptr_t)lkb_iobuffer + lkb_iobuffer_size - bootargs_size);
|
||||
paddr_t args_phys = lkb_iobuffer_phys + lkb_iobuffer_size - bootargs_size;
|
||||
bootargs_start(args, bootargs_size);
|
||||
bootargs_add_command_line(args, bootargs_size, "what what");
|
||||
/* construct a boot argument list */
|
||||
// XXX get memory smarter than this
|
||||
const size_t bootargs_size = 64*1024;
|
||||
void *args = (void *)((uintptr_t)lkb_iobuffer + lkb_iobuffer_size - bootargs_size);
|
||||
paddr_t args_phys = lkb_iobuffer_phys + lkb_iobuffer_size - bootargs_size;
|
||||
bootargs_start(args, bootargs_size);
|
||||
bootargs_add_command_line(args, bootargs_size, "what what");
|
||||
|
||||
ulong lk_args[4];
|
||||
bootargs_generate_lk_arg_values(args_phys, lk_args);
|
||||
ulong lk_args[4];
|
||||
bootargs_generate_lk_arg_values(args_phys, lk_args);
|
||||
|
||||
const void *ptr;
|
||||
const void *ptr;
|
||||
|
||||
/* sniff it to see if it's a bootimage or a raw image */
|
||||
bootimage_t *bi;
|
||||
if (bootimage_open(lkb_iobuffer, lkb_iobuffer_size, &bi) >= 0) {
|
||||
size_t len;
|
||||
/* sniff it to see if it's a bootimage or a raw image */
|
||||
bootimage_t *bi;
|
||||
if (bootimage_open(lkb_iobuffer, lkb_iobuffer_size, &bi) >= 0) {
|
||||
size_t len;
|
||||
|
||||
/* it's a bootimage */
|
||||
TRACEF("detected bootimage\n");
|
||||
/* it's a bootimage */
|
||||
TRACEF("detected bootimage\n");
|
||||
|
||||
/* find the lk image */
|
||||
if (bootimage_get_file_section(bi, TYPE_LK, &ptr, &len) >= 0) {
|
||||
TRACEF("found lk section at %p\n", ptr);
|
||||
/* find the lk image */
|
||||
if (bootimage_get_file_section(bi, TYPE_LK, &ptr, &len) >= 0) {
|
||||
TRACEF("found lk section at %p\n", ptr);
|
||||
|
||||
/* add the boot image to the argument list */
|
||||
size_t bootimage_size;
|
||||
bootimage_get_range(bi, NULL, &bootimage_size);
|
||||
/* add the boot image to the argument list */
|
||||
size_t bootimage_size;
|
||||
bootimage_get_range(bi, NULL, &bootimage_size);
|
||||
|
||||
bootargs_add_bootimage_pointer(args, bootargs_size, lkb_iobuffer_phys, bootimage_size);
|
||||
}
|
||||
} else {
|
||||
/* raw image, just chain load it directly */
|
||||
TRACEF("raw image, chainloading\n");
|
||||
bootargs_add_bootimage_pointer(args, bootargs_size, "pmem", lkb_iobuffer_phys, bootimage_size);
|
||||
}
|
||||
} else {
|
||||
/* raw image, just chain load it directly */
|
||||
TRACEF("raw image, chainloading\n");
|
||||
|
||||
ptr = lkb_iobuffer;
|
||||
}
|
||||
ptr = lkb_iobuffer;
|
||||
}
|
||||
|
||||
arch_chain_load((void *)ptr, lk_args[0], lk_args[1], lk_args[2], lk_args[3]);
|
||||
arch_chain_load((void *)ptr, lk_args[0], lk_args[1], lk_args[2], lk_args[3]);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* try to boot the system from a flash partition */
|
||||
status_t do_flash_boot(void)
|
||||
{
|
||||
status_t err;
|
||||
|
||||
/* construct a boot argument list */
|
||||
// XXX get memory smarter than this
|
||||
const size_t bootargs_size = 64*1024;
|
||||
void *args = (void *)((uintptr_t)lkb_iobuffer + lkb_iobuffer_size - bootargs_size);
|
||||
paddr_t args_phys = lkb_iobuffer_phys + lkb_iobuffer_size - bootargs_size;
|
||||
bootargs_start(args, bootargs_size);
|
||||
bootargs_add_command_line(args, bootargs_size, "what what");
|
||||
|
||||
ulong lk_args[4];
|
||||
bootargs_generate_lk_arg_values(args_phys, lk_args);
|
||||
|
||||
const void *ptr;
|
||||
|
||||
if (!ptable_found_valid()) {
|
||||
TRACEF("ptable not found\n");
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* find the system partition */
|
||||
struct ptable_entry entry;
|
||||
err = ptable_find("system", &entry);
|
||||
if (err < 0) {
|
||||
TRACEF("cannot find system partition\n");
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* get a direct pointer to the device */
|
||||
bdev_t *bdev = ptable_get_device();
|
||||
if (!bdev) {
|
||||
TRACEF("error opening boot device\n");
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* convert the bdev to a memory pointer */
|
||||
err = bio_ioctl(bdev, BIO_IOCTL_GET_MEM_MAP, (void *)&ptr);
|
||||
TRACEF("err %d, ptr %p\n", err, ptr);
|
||||
if (err < 0) {
|
||||
TRACEF("error getting direct pointer to block device\n");
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* sniff it to see if it's a bootimage or a raw image */
|
||||
bootimage_t *bi;
|
||||
if (bootimage_open((char *)ptr + entry.offset, entry.length, &bi) >= 0) {
|
||||
size_t len;
|
||||
|
||||
/* it's a bootimage */
|
||||
TRACEF("detected bootimage\n");
|
||||
|
||||
/* find the lk image */
|
||||
if (bootimage_get_file_section(bi, TYPE_LK, &ptr, &len) >= 0) {
|
||||
TRACEF("found lk section at %p\n", ptr);
|
||||
|
||||
/* add the boot image to the argument list */
|
||||
size_t bootimage_size;
|
||||
bootimage_get_range(bi, NULL, &bootimage_size);
|
||||
|
||||
bootargs_add_bootimage_pointer(args, bootargs_size, bdev->name, entry.offset, bootimage_size);
|
||||
}
|
||||
} else {
|
||||
/* did not find a bootimage, abort */
|
||||
bio_ioctl(bdev, BIO_IOCTL_PUT_MEM_MAP, NULL);
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
TRACEF("chain loading binary at %p\n", ptr);
|
||||
arch_chain_load((void *)ptr, lk_args[0], lk_args[1], lk_args[2], lk_args[3]);
|
||||
|
||||
/* put the block device back into block mode (though we never get here) */
|
||||
bio_ioctl(bdev, BIO_IOCTL_PUT_MEM_MAP, NULL);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// return NULL for success, error string for failure
|
||||
const char *lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, unsigned len) {
|
||||
struct lkb_command *lcmd;
|
||||
for (lcmd = lkb_cmd_list; lcmd; lcmd = lcmd->next) {
|
||||
if (!strcmp(lcmd->name, cmd)) {
|
||||
return lcmd->handler(lkb, arg, len, lcmd->cookie);
|
||||
}
|
||||
}
|
||||
int lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, unsigned len, const char **result)
|
||||
{
|
||||
*result = NULL;
|
||||
|
||||
if (len > lkb_iobuffer_size) {
|
||||
return "buffer too small";
|
||||
}
|
||||
if (!strcmp(cmd, "flash") || !strcmp(cmd, "erase")) {
|
||||
struct ptable_entry entry;
|
||||
bdev_t *bdev;
|
||||
struct lkb_command *lcmd;
|
||||
for (lcmd = lkb_cmd_list; lcmd; lcmd = lcmd->next) {
|
||||
if (!strcmp(lcmd->name, cmd)) {
|
||||
*result = lcmd->handler(lkb, arg, len, lcmd->cookie);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptable_find(arg, &entry) < 0) {
|
||||
size_t plen = len;
|
||||
/* doesn't exist, make one */
|
||||
if (len > lkb_iobuffer_size) {
|
||||
*result = "buffer too small";
|
||||
return -1;
|
||||
}
|
||||
if (!strcmp(cmd, "flash") || !strcmp(cmd, "erase")) {
|
||||
struct ptable_entry entry;
|
||||
bdev_t *bdev;
|
||||
|
||||
if (ptable_find(arg, &entry) < 0) {
|
||||
size_t plen = len;
|
||||
/* doesn't exist, make one */
|
||||
#if PLATFORM_ZYNQ
|
||||
/* XXX not really the right place, should be in the ptable/bio layer */
|
||||
plen = ROUNDUP(plen, 256*1024);
|
||||
/* XXX not really the right place, should be in the ptable/bio layer */
|
||||
plen = ROUNDUP(plen, 256*1024);
|
||||
#endif
|
||||
off_t off = ptable_allocate(plen, 0);
|
||||
if (off < 0) {
|
||||
return "no space to allocate partition";
|
||||
}
|
||||
off_t off = ptable_allocate(plen, 0);
|
||||
if (off < 0) {
|
||||
*result = "no space to allocate partition";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ptable_add(arg, off, plen, 0) < 0) {
|
||||
return "error creating partition";
|
||||
}
|
||||
if (ptable_add(arg, off, plen, 0) < 0) {
|
||||
*result = "error creating partition";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ptable_find(arg, &entry) < 0) {
|
||||
return "couldn't find partition after creating it";
|
||||
}
|
||||
}
|
||||
if (len > entry.length) {
|
||||
return "partition too small";
|
||||
}
|
||||
if (lkb_read(lkb, lkb_iobuffer, len)) {
|
||||
return "io error";
|
||||
}
|
||||
if (!(bdev = bio_open(bootdevice))) {
|
||||
return "bio_open failed";
|
||||
}
|
||||
if (bio_erase(bdev, entry.offset, entry.length) != (ssize_t)entry.length) {
|
||||
bio_close(bdev);
|
||||
return "bio_erase failed";
|
||||
}
|
||||
if (!strcmp(cmd, "flash")) {
|
||||
if (bio_write(bdev, lkb_iobuffer, entry.offset, len) != (ssize_t)len) {
|
||||
bio_close(bdev);
|
||||
return "bio_write failed";
|
||||
}
|
||||
}
|
||||
bio_close(bdev);
|
||||
return NULL;
|
||||
} else if (!strcmp(cmd, "remove")) {
|
||||
if (ptable_remove(arg) < 0) {
|
||||
return "remove failed";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
} else if (!strcmp(cmd, "fpga")) {
|
||||
if (ptable_find(arg, &entry) < 0) {
|
||||
*result = "couldn't find partition after creating it";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (len > entry.length) {
|
||||
*result = "partition too small";
|
||||
return -1;
|
||||
}
|
||||
if (lkb_read(lkb, lkb_iobuffer, len)) {
|
||||
*result = "io error";
|
||||
return -1;
|
||||
}
|
||||
if (!(bdev = ptable_get_device())) {
|
||||
*result = "ptable_get_device failed";
|
||||
return -1;
|
||||
}
|
||||
printf("lkboot: erasing partition of size %llu\n", entry.length);
|
||||
if (bio_erase(bdev, entry.offset, entry.length) != (ssize_t)entry.length) {
|
||||
*result = "bio_erase failed";
|
||||
return -1;
|
||||
}
|
||||
if (!strcmp(cmd, "flash")) {
|
||||
printf("lkboot: writing to partition\n");
|
||||
if (bio_write(bdev, lkb_iobuffer, entry.offset, len) != (ssize_t)len) {
|
||||
*result = "bio_write failed";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(cmd, "remove")) {
|
||||
if (ptable_remove(arg) < 0) {
|
||||
*result = "remove failed";
|
||||
return -1;
|
||||
}
|
||||
} else if (!strcmp(cmd, "fpga")) {
|
||||
#if PLATFORM_ZYNQ
|
||||
if (lkb_read(lkb, lkb_iobuffer, len)) {
|
||||
return "io error";
|
||||
}
|
||||
if (lkb_read(lkb, lkb_iobuffer, len)) {
|
||||
*result = "io error";
|
||||
return -1;
|
||||
}
|
||||
|
||||
zynq_reset_fpga();
|
||||
zynq_program_fpga(lkb_iobuffer_phys, len);
|
||||
return NULL;
|
||||
zynq_reset_fpga();
|
||||
zynq_program_fpga(lkb_iobuffer_phys, len);
|
||||
*result = NULL;
|
||||
#else
|
||||
return "no fpga";
|
||||
*result = "no fpga";
|
||||
return -1;
|
||||
#endif
|
||||
} else if (!strcmp(cmd, "boot")) {
|
||||
if (lkb_read(lkb, lkb_iobuffer, len)) {
|
||||
return "io error";
|
||||
}
|
||||
thread_resume(thread_create("boot", &do_boot, NULL,
|
||||
DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
|
||||
return NULL;
|
||||
} else if (!strcmp(cmd, "getsysparam")) {
|
||||
const void *ptr;
|
||||
size_t len;
|
||||
if (sysparam_get_ptr(arg, &ptr, &len) == 0) {
|
||||
lkb_write(lkb, ptr, len);
|
||||
}
|
||||
return NULL;
|
||||
} else if (!strcmp(cmd, "reboot")) {
|
||||
thread_resume(thread_create("reboot", &do_reboot, NULL,
|
||||
DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
|
||||
return NULL;
|
||||
} else {
|
||||
return "unknown command";
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(cmd, "boot")) {
|
||||
if (lkb_read(lkb, lkb_iobuffer, len)) {
|
||||
*result = "io error";
|
||||
return -1;
|
||||
}
|
||||
thread_resume(thread_create("boot", &do_boot, NULL,
|
||||
DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
|
||||
} else if (!strcmp(cmd, "getsysparam")) {
|
||||
const void *ptr;
|
||||
size_t len;
|
||||
if (sysparam_get_ptr(arg, &ptr, &len) == 0) {
|
||||
lkb_write(lkb, ptr, len);
|
||||
}
|
||||
} else if (!strcmp(cmd, "reboot")) {
|
||||
thread_resume(thread_create("reboot", &do_reboot, NULL,
|
||||
DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
|
||||
} else {
|
||||
*result = "unknown command";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// vim: noexpandtab
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ int lkb_write(lkb_t *lkb, const void *data, size_t len);
|
||||
// use lkb_read() to read some or all of this data
|
||||
// return NULL on success, or an asciiz string (message) for error
|
||||
typedef const char* (*lkb_handler_t)(lkb_t *lkb,
|
||||
const char *arg, unsigned len, void *cookie);
|
||||
const char *arg, unsigned len, void *cookie);
|
||||
|
||||
// cmd must be a string constant
|
||||
void lkb_register(const char *cmd, lkb_handler_t handler, void *cookie);
|
||||
|
||||
@@ -28,15 +28,29 @@
|
||||
#include <debug.h>
|
||||
#include <string.h>
|
||||
#include <pow2.h>
|
||||
#include <err.h>
|
||||
#include <assert.h>
|
||||
#include <trace.h>
|
||||
|
||||
#include <kernel/thread.h>
|
||||
#include <kernel/mutex.h>
|
||||
|
||||
#include <kernel/vm.h>
|
||||
#include <lib/minip.h>
|
||||
#include <app/lkboot.h>
|
||||
|
||||
#include "lkboot.h"
|
||||
|
||||
#if WITH_LIB_MINIP
|
||||
#include <lib/minip.h>
|
||||
#define LKBOOT_WITH_SERVER 1
|
||||
#else
|
||||
#define LKBOOT_WITH_SERVER 0
|
||||
#endif
|
||||
//#define LKBOOT_SERVER_TIMEOUT INFINITE_TIME
|
||||
#define LKBOOT_SERVER_TIMEOUT 5000
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
void *lkb_iobuffer = 0;
|
||||
paddr_t lkb_iobuffer_phys = 0;
|
||||
size_t lkb_iobuffer_size = 16*1024*1024;
|
||||
@@ -48,217 +62,249 @@ size_t lkb_iobuffer_size = 16*1024*1024;
|
||||
#define STATE_ERROR 4
|
||||
|
||||
typedef struct LKB {
|
||||
tcp_socket_t *s;
|
||||
int state;
|
||||
size_t avail;
|
||||
tcp_socket_t *s;
|
||||
int state;
|
||||
size_t avail;
|
||||
} lkb_t;
|
||||
|
||||
static volatile int busy = 0;
|
||||
extern int lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, unsigned len, const char **result);
|
||||
extern status_t do_flash_boot(void);
|
||||
|
||||
static int readx(void *s, void *_data, size_t len) {
|
||||
char *data = _data;
|
||||
while (len > 0) {
|
||||
int r = tcp_read(s, data, len);
|
||||
if (r <= 0) return -1;
|
||||
data += r;
|
||||
len -= r;
|
||||
}
|
||||
return 0;
|
||||
char *data = _data;
|
||||
while (len > 0) {
|
||||
int r = tcp_read(s, data, len);
|
||||
if (r <= 0) return -1;
|
||||
data += r;
|
||||
len -= r;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lkb_send(lkb_t *lkb, u8 opcode, const void *data, size_t len) {
|
||||
msg_hdr_t hdr;
|
||||
msg_hdr_t hdr;
|
||||
|
||||
// once we sent our OKAY or FAIL or errored out, no more writes
|
||||
if (lkb->state >= STATE_DONE) return -1;
|
||||
// once we sent our OKAY or FAIL or errored out, no more writes
|
||||
if (lkb->state >= STATE_DONE) return -1;
|
||||
|
||||
switch (opcode) {
|
||||
case MSG_OKAY:
|
||||
case MSG_FAIL:
|
||||
lkb->state = STATE_DONE;
|
||||
if (len > 0xFFFF) return -1;
|
||||
break;
|
||||
case MSG_LOG:
|
||||
if (len > 0xFFFF) return -1;
|
||||
break;
|
||||
case MSG_SEND_DATA:
|
||||
if (len > 0x10000) return -1;
|
||||
break;
|
||||
case MSG_GO_AHEAD:
|
||||
if (lkb->state == STATE_OPEN) {
|
||||
lkb->state = STATE_DATA;
|
||||
break;
|
||||
}
|
||||
len = 0;
|
||||
default:
|
||||
lkb->state = STATE_ERROR;
|
||||
opcode = MSG_FAIL;
|
||||
data = "internal error";
|
||||
len = 14;
|
||||
break;
|
||||
}
|
||||
switch (opcode) {
|
||||
case MSG_OKAY:
|
||||
case MSG_FAIL:
|
||||
lkb->state = STATE_DONE;
|
||||
if (len > 0xFFFF) return -1;
|
||||
break;
|
||||
case MSG_LOG:
|
||||
if (len > 0xFFFF) return -1;
|
||||
break;
|
||||
case MSG_SEND_DATA:
|
||||
if (len > 0x10000) return -1;
|
||||
break;
|
||||
case MSG_GO_AHEAD:
|
||||
if (lkb->state == STATE_OPEN) {
|
||||
lkb->state = STATE_DATA;
|
||||
break;
|
||||
}
|
||||
len = 0;
|
||||
default:
|
||||
lkb->state = STATE_ERROR;
|
||||
opcode = MSG_FAIL;
|
||||
data = "internal error";
|
||||
len = 14;
|
||||
break;
|
||||
}
|
||||
|
||||
hdr.opcode = opcode;
|
||||
hdr.extra = 0;
|
||||
hdr.length = (opcode == MSG_SEND_DATA) ? (len - 1) : len;
|
||||
if (tcp_write(lkb->s, &hdr, sizeof(hdr)) != sizeof(&hdr)) {
|
||||
printf("xmit hdr fail\n");
|
||||
lkb->state = STATE_ERROR;
|
||||
return -1;
|
||||
}
|
||||
if (len && (tcp_write(lkb->s, data, len) != (ssize_t)len)) {
|
||||
printf("xmit data fail\n");
|
||||
lkb->state = STATE_ERROR;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
hdr.opcode = opcode;
|
||||
hdr.extra = 0;
|
||||
hdr.length = (opcode == MSG_SEND_DATA) ? (len - 1) : len;
|
||||
if (tcp_write(lkb->s, &hdr, sizeof(hdr)) != sizeof(&hdr)) {
|
||||
printf("xmit hdr fail\n");
|
||||
lkb->state = STATE_ERROR;
|
||||
return -1;
|
||||
}
|
||||
if (len && (tcp_write(lkb->s, data, len) != (ssize_t)len)) {
|
||||
printf("xmit data fail\n");
|
||||
lkb->state = STATE_ERROR;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define lkb_okay(lkb) lkb_send(lkb, MSG_OKAY, NULL, 0)
|
||||
#define lkb_fail(lkb, msg) lkb_send(lkb, MSG_FAIL, msg, strlen(msg))
|
||||
|
||||
int lkb_write(lkb_t *lkb, const void *_data, size_t len) {
|
||||
const char *data = _data;
|
||||
while (len > 0) {
|
||||
size_t xfer = (len > 65536) ? 65536 : len;
|
||||
if (lkb_send(lkb, MSG_SEND_DATA, data, xfer)) return -1;
|
||||
len -= xfer;
|
||||
data += xfer;
|
||||
}
|
||||
return 0;
|
||||
const char *data = _data;
|
||||
while (len > 0) {
|
||||
size_t xfer = (len > 65536) ? 65536 : len;
|
||||
if (lkb_send(lkb, MSG_SEND_DATA, data, xfer)) return -1;
|
||||
len -= xfer;
|
||||
data += xfer;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lkb_read(lkb_t *lkb, void *_data, size_t len) {
|
||||
char *data = _data;
|
||||
char *data = _data;
|
||||
|
||||
printf("lkb_read %d\n", len);
|
||||
if (lkb->state == STATE_RESP) {
|
||||
return 0;
|
||||
}
|
||||
if (lkb->state == STATE_OPEN) {
|
||||
if (lkb_send(lkb, MSG_GO_AHEAD, NULL, 0)) return -1;
|
||||
}
|
||||
while (len > 0) {
|
||||
if (lkb->avail == 0) {
|
||||
msg_hdr_t hdr;
|
||||
if (readx(lkb->s, &hdr, sizeof(hdr))) goto fail;
|
||||
if (hdr.opcode == MSG_END_DATA) {
|
||||
lkb->state = STATE_RESP;
|
||||
return -1;
|
||||
}
|
||||
if (hdr.opcode != MSG_SEND_DATA) goto fail;
|
||||
lkb->avail = ((size_t) hdr.length) + 1;
|
||||
}
|
||||
if (lkb->avail >= len) {
|
||||
if (readx(lkb->s, data, len)) goto fail;
|
||||
lkb->avail -= len;
|
||||
return 0;
|
||||
}
|
||||
if (readx(lkb->s, data, lkb->avail)) {
|
||||
lkb->state = STATE_ERROR;
|
||||
return -1;
|
||||
}
|
||||
data += lkb->avail;
|
||||
len -= lkb->avail;
|
||||
lkb->avail = 0;
|
||||
}
|
||||
return 0;
|
||||
printf("lkb_read %d\n", len);
|
||||
if (lkb->state == STATE_RESP) {
|
||||
return 0;
|
||||
}
|
||||
if (lkb->state == STATE_OPEN) {
|
||||
if (lkb_send(lkb, MSG_GO_AHEAD, NULL, 0)) return -1;
|
||||
}
|
||||
while (len > 0) {
|
||||
if (lkb->avail == 0) {
|
||||
msg_hdr_t hdr;
|
||||
if (readx(lkb->s, &hdr, sizeof(hdr))) goto fail;
|
||||
if (hdr.opcode == MSG_END_DATA) {
|
||||
lkb->state = STATE_RESP;
|
||||
return -1;
|
||||
}
|
||||
if (hdr.opcode != MSG_SEND_DATA) goto fail;
|
||||
lkb->avail = ((size_t) hdr.length) + 1;
|
||||
}
|
||||
if (lkb->avail >= len) {
|
||||
if (readx(lkb->s, data, len)) goto fail;
|
||||
lkb->avail -= len;
|
||||
return 0;
|
||||
}
|
||||
if (readx(lkb->s, data, lkb->avail)) {
|
||||
lkb->state = STATE_ERROR;
|
||||
return -1;
|
||||
}
|
||||
data += lkb->avail;
|
||||
len -= lkb->avail;
|
||||
lkb->avail = 0;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
lkb->state = STATE_ERROR;
|
||||
return -1;
|
||||
lkb->state = STATE_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, unsigned len);
|
||||
static status_t handle_txn(tcp_socket_t *s)
|
||||
{
|
||||
msg_hdr_t hdr;
|
||||
lkb_t lkb;
|
||||
char cmd[128];
|
||||
char *arg;
|
||||
int err;
|
||||
const char *result;
|
||||
unsigned len;
|
||||
|
||||
static int handle_txn(void *s) {
|
||||
msg_hdr_t hdr;
|
||||
lkb_t lkb;
|
||||
char cmd[128];
|
||||
char *arg;
|
||||
const char *err;
|
||||
unsigned len;
|
||||
if (readx(s, &hdr, sizeof(hdr))) goto fail;
|
||||
if (hdr.opcode != MSG_CMD) goto fail;
|
||||
if (hdr.length > 127) goto fail;
|
||||
if (readx(s, cmd, hdr.length)) goto fail;
|
||||
cmd[hdr.length] = 0;
|
||||
|
||||
if (readx(s, &hdr, sizeof(hdr))) goto fail;
|
||||
if (hdr.opcode != MSG_CMD) goto fail;
|
||||
if (hdr.length > 127) goto fail;
|
||||
if (readx(s, cmd, hdr.length)) goto fail;
|
||||
cmd[hdr.length] = 0;
|
||||
printf("lkboot: recv '%s'\n", cmd);
|
||||
|
||||
printf("lkboot: recv '%s'\n", cmd);
|
||||
if (!(arg = strchr(cmd, ':'))) goto fail;
|
||||
*arg++ = 0;
|
||||
len = atoul(arg);
|
||||
if (!(arg = strchr(arg, ':'))) goto fail;
|
||||
arg++;
|
||||
|
||||
if (!(arg = strchr(cmd, ':'))) goto fail;
|
||||
*arg++ = 0;
|
||||
len = atoul(arg);
|
||||
if (!(arg = strchr(arg, ':'))) goto fail;
|
||||
arg++;
|
||||
lkb.s = s;
|
||||
lkb.state = STATE_OPEN;
|
||||
lkb.avail = 0;
|
||||
err = lkb_handle_command(&lkb, cmd, arg, len, &result);
|
||||
if (err >= 0) {
|
||||
lkb_okay(&lkb);
|
||||
} else {
|
||||
lkb_fail(&lkb, result);
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
|
||||
lkb.s = s;
|
||||
lkb.state = STATE_OPEN;
|
||||
lkb.avail = 0;
|
||||
err = lkb_handle_command(&lkb, cmd, arg, len);
|
||||
if (err == 0) {
|
||||
lkb_okay(&lkb);
|
||||
} else {
|
||||
lkb_fail(&lkb, err);
|
||||
}
|
||||
fail:
|
||||
tcp_close(s);
|
||||
busy = 0;
|
||||
return 0;
|
||||
tcp_close(s);
|
||||
return ERR_IO;
|
||||
}
|
||||
|
||||
static int lkboot_server(void *arg) {
|
||||
tcp_socket_t *listen_socket;
|
||||
tcp_socket_t *s;
|
||||
static void lkboot_task(const struct app_descriptor *app, void *args)
|
||||
{
|
||||
#if LKBOOT_WITH_SERVER
|
||||
tcp_socket_t *listen_socket = NULL;
|
||||
bool wait_forever = (LKBOOT_SERVER_TIMEOUT == INFINITE_TIME);
|
||||
|
||||
if (tcp_open_listen(&listen_socket, 1023) < 0) {
|
||||
printf("lkboot: error opening listen socket\n");
|
||||
return -1;
|
||||
}
|
||||
// allocate memory for the server
|
||||
if (vmm_alloc_contiguous(vmm_get_kernel_aspace(), "lkboot_iobuf",
|
||||
lkb_iobuffer_size, &lkb_iobuffer, log2_uint(lkb_iobuffer_size), 0,
|
||||
ARCH_MMU_FLAG_UNCACHED) < 0) {
|
||||
goto regboot;
|
||||
}
|
||||
if (arch_mmu_query((u32) lkb_iobuffer, &lkb_iobuffer_phys, NULL) < 0) {
|
||||
goto regboot;
|
||||
}
|
||||
printf("lkboot: iobuffer %p (phys 0x%lx)\n", lkb_iobuffer, lkb_iobuffer_phys);
|
||||
|
||||
for (;;) {
|
||||
if (tcp_accept(listen_socket, &s) < 0) {
|
||||
continue;
|
||||
}
|
||||
if (busy) {
|
||||
printf("lkboot: concurrent connections disallowed\n");
|
||||
tcp_close(s);
|
||||
continue;
|
||||
}
|
||||
busy = 1;
|
||||
thread_detach_and_resume(
|
||||
thread_create("lkboot_txn", &handle_txn, s,
|
||||
DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
serverloop:
|
||||
/* open the server's socket */
|
||||
if (tcp_open_listen(&listen_socket, 1023) < 0) {
|
||||
printf("lkboot: error opening listen socket\n");
|
||||
goto regboot;
|
||||
}
|
||||
|
||||
static void lkboot_init(const struct app_descriptor *app) {
|
||||
printf("lkboot: init\n");
|
||||
/* run the main lkserver loop */
|
||||
printf("lkboot: starting network server\n");
|
||||
lk_time_t t = current_time();
|
||||
for (;;) {
|
||||
tcp_socket_t *s;
|
||||
|
||||
if (vmm_alloc_contiguous(vmm_get_kernel_aspace(), "lkboot_iobuf",
|
||||
lkb_iobuffer_size, &lkb_iobuffer, log2_uint(16*1024*1024), 0,
|
||||
ARCH_MMU_FLAG_UNCACHED) < 0) {
|
||||
goto fail_alloc;
|
||||
}
|
||||
if (arch_mmu_query((u32) lkb_iobuffer, &lkb_iobuffer_phys, NULL) < 0) {
|
||||
goto fail_alloc;
|
||||
}
|
||||
printf("lkboot: iobuffer %p (phys 0x%lx)\n", lkb_iobuffer, lkb_iobuffer_phys);
|
||||
/* calculate how much longer we will wait */
|
||||
lk_time_t timeout = INFINITE_TIME;
|
||||
if (!wait_forever) {
|
||||
timeout = current_time() - t;
|
||||
if (timeout >= LKBOOT_SERVER_TIMEOUT)
|
||||
break;
|
||||
timeout = LKBOOT_SERVER_TIMEOUT - timeout;
|
||||
}
|
||||
|
||||
thread_detach_and_resume(
|
||||
thread_create("lkbootserver", &lkboot_server, NULL,
|
||||
DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
|
||||
return;
|
||||
/* wait for a new connection */
|
||||
if (tcp_accept_timeout(listen_socket, &s, timeout) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fail_alloc:
|
||||
printf("lkboot: failed to allocate iobuffer\n");
|
||||
DEBUG_ASSERT(s);
|
||||
|
||||
/* handle the command and close it */
|
||||
handle_txn(s);
|
||||
tcp_close(s);
|
||||
|
||||
/* after the first command, stay in the server loop forever */
|
||||
if (!wait_forever) {
|
||||
wait_forever = true;
|
||||
printf("lkboot: handled command, staying in server loop\n");
|
||||
}
|
||||
}
|
||||
|
||||
tcp_close(listen_socket);
|
||||
|
||||
printf("lkboot: network server timed out\n");
|
||||
#endif
|
||||
|
||||
regboot:
|
||||
printf("lkboot: trying to boot from flash...\n");
|
||||
|
||||
status_t err = do_flash_boot();
|
||||
TRACEF("do_flash_boot returns %d\n", err);
|
||||
|
||||
#if LKBOOT_WITH_SERVER
|
||||
if (err < 0) {
|
||||
TRACEF("restarting network server\n");
|
||||
wait_forever = true;
|
||||
goto serverloop;
|
||||
}
|
||||
#endif
|
||||
|
||||
TRACEF("nothing to do, exiting\n");
|
||||
}
|
||||
|
||||
APP_START(lkboot)
|
||||
.init = lkboot_init,
|
||||
.flags = 0,
|
||||
.entry = lkboot_task,
|
||||
.flags = 0,
|
||||
APP_END
|
||||
|
||||
// vim: noexpandtab
|
||||
|
||||
@@ -23,40 +23,40 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct {
|
||||
unsigned char opcode;
|
||||
unsigned char extra;
|
||||
unsigned short length;
|
||||
unsigned char opcode;
|
||||
unsigned char extra;
|
||||
unsigned short length;
|
||||
} msg_hdr_t;
|
||||
|
||||
// unless otherwise specified, extra is always zero.
|
||||
|
||||
#define MSG_OKAY 0x00
|
||||
#define MSG_OKAY 0x00
|
||||
// length must be zero.
|
||||
// server indicates command was successful.
|
||||
|
||||
#define MSG_FAIL 0xFF
|
||||
#define MSG_FAIL 0xFF
|
||||
// length may be nonzero, if so data is a human readable error message
|
||||
// extra may be nonzero, if so it is a more specific error code
|
||||
|
||||
#define MSG_LOG 0xFE
|
||||
#define MSG_LOG 0xFE
|
||||
// data contains human readable log message from server
|
||||
// server may issue these at any time
|
||||
|
||||
#define MSG_GO_AHEAD 0x01
|
||||
#define MSG_GO_AHEAD 0x01
|
||||
// length must be zero
|
||||
// server indicates that command was valid and it is ready for data
|
||||
// client should send MSG_SEND_DATA messages to transfer data
|
||||
|
||||
#define MSG_CMD 0x40
|
||||
#define MSG_CMD 0x40
|
||||
// length must be greater than zero
|
||||
// data will contain an ascii command
|
||||
// server may reject excessively large commands
|
||||
|
||||
#define MSG_SEND_DATA 0x41
|
||||
#define MSG_SEND_DATA 0x41
|
||||
// client sends data to server
|
||||
// length is datalen -1 (to allow for full 64k chunks)
|
||||
|
||||
#define MSG_END_DATA 0x42
|
||||
#define MSG_END_DATA 0x42
|
||||
// client ends data stream
|
||||
// server will then respond with MSG_OKAY or MSG_FAIL
|
||||
|
||||
@@ -78,5 +78,3 @@ typedef struct {
|
||||
//
|
||||
// C: MSG_CMD "reboot:0:"
|
||||
// S: MSG_OKAY
|
||||
|
||||
// vim: noexpandtab
|
||||
|
||||
Reference in New Issue
Block a user