[app][lkboot] network bootloader server
This commit is contained in:
102
app/lkboot/commands.c
Normal file
102
app/lkboot/commands.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Brian Swetland
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include <platform.h>
|
||||
#include <stdio.h>
|
||||
#include <debug.h>
|
||||
#include <string.h>
|
||||
#include <endian.h>
|
||||
|
||||
#include <lib/ptable.h>
|
||||
#include <lib/bio.h>
|
||||
#include <lib/sysparam.h>
|
||||
|
||||
#if PLATFORM_ZYNQ
|
||||
#include <platform/fpga.h>
|
||||
#endif
|
||||
|
||||
#define bootdevice "spi0"
|
||||
|
||||
typedef struct LKB lkb_t;
|
||||
|
||||
// returns 0 on success, -1 on failure (short read or io error)
|
||||
int lkb_read(lkb_t *lkb, void *_data, size_t len);
|
||||
|
||||
extern void *lkb_iobuffer;
|
||||
extern paddr_t lkb_iobuffer_phys;
|
||||
extern size_t lkb_iobuffer_size;
|
||||
|
||||
// return NULL for success, error string for failure
|
||||
const char *lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, unsigned len) {
|
||||
if (len > lkb_iobuffer_size) {
|
||||
return "buffer too small";
|
||||
}
|
||||
if (!strcmp(cmd, "flash") || !strcmp(cmd, "erase")) {
|
||||
struct ptable_entry entry;
|
||||
bdev_t *bdev;
|
||||
if (ptable_find(arg, &entry) < 0) {
|
||||
return "no such partition";
|
||||
}
|
||||
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) != entry.length) {
|
||||
bio_close(bdev);
|
||||
return "bio_erase failed";
|
||||
}
|
||||
if (!strcmp(cmd, "flash")) {
|
||||
if (bio_write(bdev, lkb_iobuffer, entry.offset, len) != len) {
|
||||
bio_close(bdev);
|
||||
return "bio_write failed";
|
||||
}
|
||||
}
|
||||
bio_close(bdev);
|
||||
return NULL;
|
||||
} else if (!strcmp(cmd, "fpga")) {
|
||||
#if PLATFORM_ZYNQ
|
||||
int n;
|
||||
unsigned *x = lkb_iobuffer;
|
||||
if (lkb_read(lkb, lkb_iobuffer, len)) {
|
||||
return "io error";
|
||||
}
|
||||
for (n = 0; n < len; n+= 4) {
|
||||
*x = SWAP_32(*x);
|
||||
x++;
|
||||
}
|
||||
zynq_reset_fpga();
|
||||
zynq_program_fpga(lkb_iobuffer_phys, len);
|
||||
return NULL;
|
||||
#else
|
||||
return "no fpga";
|
||||
#endif
|
||||
} else {
|
||||
return "unknown command";
|
||||
}
|
||||
}
|
||||
243
app/lkboot/lkboot.c
Normal file
243
app/lkboot/lkboot.c
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Brian Swetland
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <app.h>
|
||||
|
||||
#include <platform.h>
|
||||
#include <stdio.h>
|
||||
#include <debug.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <kernel/thread.h>
|
||||
|
||||
#include <kernel/vm.h>
|
||||
#include <lib/minip.h>
|
||||
|
||||
#include "lkboot.h"
|
||||
|
||||
void *lkb_iobuffer = 0;
|
||||
paddr_t lkb_iobuffer_phys = 0;
|
||||
size_t lkb_iobuffer_size = 16*1024*1024;
|
||||
|
||||
#define STATE_OPEN 0
|
||||
#define STATE_DATA 1
|
||||
#define STATE_RESP 2
|
||||
#define STATE_DONE 3
|
||||
#define STATE_ERROR 4
|
||||
|
||||
typedef struct LKB {
|
||||
void *s;
|
||||
int state;
|
||||
size_t avail;
|
||||
} lkb_t;
|
||||
|
||||
static volatile int busy = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int lkb_send(lkb_t *lkb, u8 opcode, const void *data, size_t len) {
|
||||
msg_hdr_t hdr;
|
||||
if (len > 0xFFFF) 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;
|
||||
break;
|
||||
case MSG_LOG:
|
||||
break;
|
||||
case MSG_GO_AHEAD:
|
||||
if (lkb->state == STATE_OPEN) {
|
||||
lkb->state = STATE_DATA;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
lkb->state = STATE_ERROR;
|
||||
opcode = MSG_FAIL;
|
||||
data = "internal error";
|
||||
len = 14;
|
||||
break;
|
||||
}
|
||||
|
||||
hdr.opcode = opcode;
|
||||
hdr.extra = 0;
|
||||
hdr.length = 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) != 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_read(lkb_t *lkb, void *_data, size_t len) {
|
||||
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;
|
||||
|
||||
fail:
|
||||
lkb->state = STATE_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, 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;
|
||||
|
||||
printf("lkboot: recv '%s'\n", cmd);
|
||||
|
||||
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);
|
||||
if (err == 0) {
|
||||
lkb_okay(&lkb);
|
||||
} else {
|
||||
lkb_fail(&lkb, err);
|
||||
}
|
||||
fail:
|
||||
tcp_close(s);
|
||||
busy = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lkboot_server(void *arg) {
|
||||
void *listen_socket;
|
||||
void *s;
|
||||
|
||||
if (tcp_open_listen(&listen_socket, 1023) < 0) {
|
||||
printf("lkboot: error opening listen socket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void lkboot_init(const struct app_descriptor *app) {
|
||||
printf("lkboot: init\n");
|
||||
|
||||
if (vmm_alloc_contiguous(vmm_get_kernel_aspace(), "lkboot_iobuf",
|
||||
lkb_iobuffer_size, &lkb_iobuffer, 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);
|
||||
|
||||
thread_detach_and_resume(
|
||||
thread_create("lkbootserver", &lkboot_server, NULL,
|
||||
DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
|
||||
return;
|
||||
|
||||
fail_alloc:
|
||||
printf("lkboot: failed to allocate iobuffer\n");
|
||||
}
|
||||
|
||||
APP_START(lkboot)
|
||||
.init = lkboot_init,
|
||||
.flags = 0,
|
||||
APP_END
|
||||
79
app/lkboot/lkboot.h
Normal file
79
app/lkboot/lkboot.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Brian Swetland
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
unsigned char opcode;
|
||||
unsigned char extra;
|
||||
unsigned short length;
|
||||
} msg_hdr_t;
|
||||
|
||||
// unless otherwise specified, extra is always zero.
|
||||
|
||||
#define MSG_OKAY 0x00
|
||||
// length must be zero.
|
||||
// server indicates command was successful.
|
||||
|
||||
#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
|
||||
// data contains human readable log message from server
|
||||
// server may issue these at any time
|
||||
|
||||
#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
|
||||
// length must be greater than zero
|
||||
// data will contain an ascii command
|
||||
// server may reject excessively large commands
|
||||
|
||||
#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
|
||||
// client ends data stream
|
||||
// server will then respond with MSG_OKAY or MSG_FAIL
|
||||
|
||||
// command strings are in the form of
|
||||
// <command> ':' <decimal-datalen> ':' <optional-arguments>
|
||||
|
||||
// example:
|
||||
// C: MSG_CMD "flash:32768:bootloader"
|
||||
// S: MSG_GO_AHEAD
|
||||
// C: MSG_SEND_DATA 16384 ...
|
||||
// C: MSG_SEND_DATA 16384 ...
|
||||
// C: MSG_END_DATA
|
||||
// S: MSG_LOG "erasing sectors"
|
||||
// S: MSG_LOG "writing sectors"
|
||||
// S: MSG_OKAY
|
||||
//
|
||||
// C: MSG_CMD "eraese:0:bootloader"
|
||||
// S: MSG_FAIL "unknown command 'eraese'"
|
||||
//
|
||||
// C: MSG_CMD "reboot:0:"
|
||||
// S: MSG_OKAY
|
||||
13
app/lkboot/rules.mk
Normal file
13
app/lkboot/rules.mk
Normal file
@@ -0,0 +1,13 @@
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
MODULE := $(LOCAL_DIR)
|
||||
|
||||
MODULE_DEPS += \
|
||||
lib/console \
|
||||
lib/minip
|
||||
|
||||
MODULE_SRCS += \
|
||||
$(LOCAL_DIR)/lkboot.c \
|
||||
$(LOCAL_DIR)/commands.c
|
||||
|
||||
include make/module.mk
|
||||
Reference in New Issue
Block a user