[app][lkboot] modularlize lkboot, add secondary DCC channel, remove large buffer

Change-Id: I5a59f06c909722fe7269aa0991da10fa1ea8196e
This commit is contained in:
Travis Geiselbrecht
2015-04-23 19:32:47 -07:00
parent 79e69c75e1
commit 3764cb3a2d
8 changed files with 724 additions and 188 deletions

View File

@@ -32,8 +32,10 @@
#include <arch.h>
#include <err.h>
#include <trace.h>
#include <pow2.h>
#include <kernel/thread.h>
#include <kernel/vm.h>
#include <lib/bio.h>
#include <lib/bootargs.h>
@@ -45,16 +47,13 @@
#if PLATFORM_ZYNQ
#include <platform/fpga.h>
#include <platform/zynq.h>
#endif
#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;
@@ -81,16 +80,63 @@ static int do_reboot(void *arg) {
return 0;
}
static int do_boot(void *arg) {
struct chainload_args {
void *func;
ulong args[4];
};
static int chainload_thread(void *arg)
{
struct chainload_args *args = (struct chainload_args *)arg;
thread_sleep(250);
TRACEF("chain loading address %p, args 0x%lx 0x%lx 0x%lx 0x%lx\n",
args->func, args->args[0], args->args[1], args->args[2], args->args[3]);
arch_chain_load((void *)args->func, args->args[0], args->args[1], args->args[2], args->args[3]);
for (;;);
}
static int do_boot(lkb_t *lkb, size_t len, const char **result)
{
LTRACEF("lkb %p, len %zu, result %p\n", lkb, len, result);
void *buf;
paddr_t buf_phys;
if (vmm_alloc_contiguous(vmm_get_kernel_aspace(), "lkboot_iobuf",
len, &buf, log2_uint(1024*1024), 0, ARCH_MMU_FLAG_UNCACHED) < 0) {
*result = "not enough memory";
return -1;
}
arch_mmu_query((vaddr_t)buf, &buf_phys, NULL);
LTRACEF("iobuffer %p (phys 0x%lx)\n", buf, buf_phys);
if (lkb_read(lkb, buf, len)) {
*result = "io error";
// XXX free buffer here
return -1;
}
/* construct a boot argument list */
// XXX get memory smarter than this
const size_t bootargs_size = 64*1024;
const size_t bootargs_size = PAGE_SIZE;
#if 0
void *args = (void *)((uintptr_t)lkb_iobuffer + lkb_iobuffer_size - bootargs_size);
paddr_t args_phys = lkb_iobuffer_phys + lkb_iobuffer_size - bootargs_size;
#elif PLATFORM_ZYNQ
/* grab the top page of sram */
/* XXX do this better */
paddr_t args_phys = SRAM_BASE + SRAM_SIZE - bootargs_size;
void *args = paddr_to_kvaddr(args_phys);
#else
#error need better way
#endif
LTRACEF("boot args %p, phys 0x%lx, len %zu\n", args, args_phys, bootargs_size);
bootargs_start(args, bootargs_size);
bootargs_add_command_line(args, bootargs_size, "what what");
arch_clean_cache_range((vaddr_t)args, bootargs_size);
ulong lk_args[4];
bootargs_generate_lk_arg_values(args_phys, lk_args);
@@ -99,7 +145,7 @@ static int do_boot(void *arg) {
/* 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) {
if (bootimage_open(buf, len, &bi) >= 0) {
size_t len;
/* it's a bootimage */
@@ -113,16 +159,26 @@ static int do_boot(void *arg) {
size_t bootimage_size;
bootimage_get_range(bi, NULL, &bootimage_size);
bootargs_add_bootimage_pointer(args, bootargs_size, "pmem", lkb_iobuffer_phys, bootimage_size);
bootargs_add_bootimage_pointer(args, bootargs_size, "pmem", buf_phys, bootimage_size);
}
} else {
/* raw image, just chain load it directly */
TRACEF("raw image, chainloading\n");
ptr = lkb_iobuffer;
ptr = buf;
}
arch_chain_load((void *)ptr, lk_args[0], lk_args[1], lk_args[2], lk_args[3]);
/* start a boot thread to complete the startup */
static struct chainload_args cl_args;
cl_args.func = (void *)ptr;
cl_args.args[0] = lk_args[0];
cl_args.args[1] = lk_args[1];
cl_args.args[2] = lk_args[2];
cl_args.args[3] = lk_args[3];
thread_resume(thread_create("boot", &chainload_thread, &cl_args,
DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
return 0;
}
@@ -132,13 +188,26 @@ status_t do_flash_boot(void)
{
status_t err;
LTRACE_ENTRY;
/* construct a boot argument list */
// XXX get memory smarter than this
const size_t bootargs_size = 64*1024;
const size_t bootargs_size = PAGE_SIZE;
#if 0
/* old code */
void *args = (void *)((uintptr_t)lkb_iobuffer + lkb_iobuffer_size - bootargs_size);
paddr_t args_phys = lkb_iobuffer_phys + lkb_iobuffer_size - bootargs_size;
#elif PLATFORM_ZYNQ
/* grab the top page of sram */
paddr_t args_phys = SRAM_BASE + SRAM_SIZE - bootargs_size;
void *args = paddr_to_kvaddr(args_phys);
#else
#error need better way
#endif
LTRACEF("boot args %p, phys 0x%lx, len %zu\n", args, args_phys, bootargs_size);
bootargs_start(args, bootargs_size);
bootargs_add_command_line(args, bootargs_size, "what what");
arch_clean_cache_range((vaddr_t)args, bootargs_size);
ulong lk_args[4];
bootargs_generate_lk_arg_values(args_phys, lk_args);
@@ -207,7 +276,7 @@ status_t do_flash_boot(void)
}
// return NULL for success, error string for failure
int lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, unsigned len, const char **result)
int lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, size_t len, const char **result)
{
*result = NULL;
@@ -219,10 +288,6 @@ int lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, unsigned le
}
}
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;
@@ -254,25 +319,49 @@ int lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, unsigned le
*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";
void *buf = malloc(bdev->block_size);
if (!buf) {
*result = "memory allocation failed";
return -1;
}
size_t pos = 0;
while (pos < len) {
size_t toread = MIN(len - pos, bdev->block_size);
LTRACEF("offset %zu, toread %zu\n", pos, toread);
if (lkb_read(lkb, buf, toread)) {
*result = "io error";
free(buf);
return -1;
}
if (bio_write(bdev, buf, entry.offset + pos, toread) != (ssize_t)toread) {
*result = "bio_write failed";
free(buf);
return -1;
}
pos += toread;
}
free(buf);
}
} else if (!strcmp(cmd, "remove")) {
if (ptable_remove(arg) < 0) {
@@ -281,25 +370,41 @@ int lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, unsigned le
}
} else if (!strcmp(cmd, "fpga")) {
#if PLATFORM_ZYNQ
if (lkb_read(lkb, lkb_iobuffer, len)) {
*result = "io error";
void *buf = malloc(len);
if (!buf) {
*result = "error allocating buffer";
return -1;
}
/* translate to physical address */
paddr_t pa = kvaddr_to_paddr(buf);
if (pa == 0) {
*result = "error allocating buffer";
free(buf);
return -1;
}
if (lkb_read(lkb, buf, len)) {
*result = "io error";
free(buf);
return -1;
}
/* make sure the cache is flushed for this buffer for DMA coherency purposes */
arch_clean_cache_range((vaddr_t)buf, len);
/* program the fpga */
zynq_reset_fpga();
zynq_program_fpga(lkb_iobuffer_phys, len);
*result = NULL;
zynq_program_fpga(pa, len);
free(buf);
#else
*result = "no fpga";
return -1;
#endif
} 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));
return do_boot(lkb, len, result);
} else if (!strcmp(cmd, "getsysparam")) {
const void *ptr;
size_t len;

259
app/lkboot/dcc.c Normal file
View File

@@ -0,0 +1,259 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* 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 "lkboot.h"
#include <stdio.h>
#include <debug.h>
#include <string.h>
#include <compiler.h>
#include <err.h>
#include <assert.h>
#include <trace.h>
#include <stdlib.h>
#include <lib/cbuf.h>
#include <app/lkboot.h>
#include <arch/arm/dcc.h>
#include <arch/mmu.h>
#include <kernel/mutex.h>
#include "pdcc.h"
#define LOCAL_TRACE 0
static struct pdcc_buffer_descriptor buffer_desc __ALIGNED(256);
static paddr_t buffer_desc_phys;
#define DCC_BUFLEN 256
static uint8_t htod_buffer[DCC_BUFLEN] __ALIGNED(CACHE_LINE);
static uint8_t dtoh_buffer[DCC_BUFLEN] __ALIGNED(CACHE_LINE);
static uint htod_index;
static uint htod_pos;
static bool dtoh_filled;
static void send_pdcc_command(uint32_t opcode, uint32_t data)
{
uint32_t word;
word = PDCC_VALID |
((opcode & 0x7f) << PDCC_OPCODE_SHIFT) |
(data & 0x00ffffff);
// XXX may block forever
LTRACEF("sending 0x%x\n", word);
arm_dcc_write(&word, 1, INFINITE_TIME);
}
static void send_buffer_header(void)
{
send_pdcc_command(PDCC_OP_BUF_HEADER, buffer_desc_phys / 256);
}
static void send_reset(void)
{
send_pdcc_command(PDCC_OP_RESET, 0);
}
static void send_out_index_update(uint32_t index)
{
send_pdcc_command(PDCC_OP_UPDATE_OUT_INDEX, index);
}
static void send_buffer_consumed(void)
{
send_pdcc_command(PDCC_OP_CONSUMED_IN, 0);
}
#define DCC_PROCESS_RESET 1
static int dcc_process_opcode(uint32_t word)
{
int ret = 0;
if (word & PDCC_VALID) {
uint32_t opcode = PDCC_OPCODE(word);
uint32_t data = PDCC_DATA(word);
LTRACEF("word 0x%x, opcode 0x%x, data 0x%x\n", word, opcode, data);
switch (opcode) {
case PDCC_OP_RESET:
htod_index = 0;
htod_pos = 0;
dtoh_filled = false;
// try to send the buffer header
send_buffer_header();
ret = DCC_PROCESS_RESET;
break;
case PDCC_OP_BUF_HEADER:
// we shouldn't get this
break;
case PDCC_OP_UPDATE_OUT_INDEX:
if (data > DCC_BUFLEN) {
// out of range
send_reset();
} else {
htod_index = data;
htod_pos = 0;
arch_invalidate_cache_range((vaddr_t)htod_buffer, DCC_BUFLEN);
}
break;
case PDCC_OP_CONSUMED_IN:
arch_invalidate_cache_range((vaddr_t)dtoh_buffer, DCC_BUFLEN);
dtoh_filled = false;
break;
default:
TRACEF("bad opcode from host 0x%x\n", opcode);
send_reset();
}
}
return ret;
}
static ssize_t dcc_read(void *unused, void *_data, size_t len)
{
unsigned char *data = _data;
size_t pos = 0;
uint32_t dcc;
LTRACEF("buf %p, len %zu, htod_pos %u, htod_index %u\n", _data, len, htod_pos, htod_index);
lk_time_t timeout = 0; // first dcc command should be with no timeout
while (pos < len) {
// process a dcc command
ssize_t err = arm_dcc_read(&dcc, 1, timeout);
if (err > 0) {
err = dcc_process_opcode(dcc);
if (err == DCC_PROCESS_RESET) {
return ERR_IO;
}
}
// see if there is any data in the incoming buffer
if (htod_index > 0) {
size_t tocopy = MIN(htod_index - htod_pos, len - pos);
memcpy(&data[pos], &htod_buffer[htod_pos], tocopy);
pos += tocopy;
htod_pos += tocopy;
// if we consumed everything, tell the host we're done with the buffer
if (htod_pos == htod_index) {
send_buffer_consumed();
htod_index = 0;
htod_pos = 0;
arch_invalidate_cache_range((vaddr_t)htod_buffer, DCC_BUFLEN);
}
}
timeout = 1000;
}
return 0;
}
static ssize_t dcc_write(void *unused, const void *_data, size_t len)
{
const unsigned char *data = _data;
size_t pos = 0;
LTRACEF("buf %p, len %zu\n", _data, len);
while (pos < len) {
LTRACEF("pos %zu, len %zu, dtoh_filled %d\n", pos, len, dtoh_filled);
if (!dtoh_filled) {
// put as much data as we can in the outgoing buffer
size_t tocopy = MIN(len, DCC_BUFLEN);
LTRACEF("tocopy %zu\n", tocopy);
memcpy(dtoh_buffer, data, tocopy);
arch_clean_cache_range((vaddr_t)dtoh_buffer, DCC_BUFLEN);
send_out_index_update(tocopy);
dtoh_filled = true;
pos += tocopy;
}
// process a dcc command
uint32_t dcc;
ssize_t err = arm_dcc_read(&dcc, 1, 1000);
if (err > 0) {
err = dcc_process_opcode(dcc);
if (err == DCC_PROCESS_RESET) {
return ERR_IO;
}
}
}
return pos;
}
lkb_t *lkboot_check_dcc_open(void)
{
lkb_t *lkb = NULL;
// read a dcc op and process it
{
uint32_t dcc;
ssize_t err = arm_dcc_read(&dcc, 1, 0);
if (err > 0) {
err = dcc_process_opcode(dcc);
}
}
if (htod_index > 0) {
// we have data, construct a lkb and return it
LTRACEF("we have data on dcc, starting command handler\n");
lkb = lkboot_create_lkb(NULL, dcc_read, dcc_write);
}
return lkb;
}
void lkboot_dcc_init(void)
{
paddr_t pa;
status_t err;
buffer_desc.version = PDCC_VERSION;
err = arch_mmu_query((vaddr_t)htod_buffer, &pa, NULL);
DEBUG_ASSERT(err == NO_ERROR);
buffer_desc.htod_buffer_phys = pa;
buffer_desc.htod_buffer_len = DCC_BUFLEN;
err = arch_mmu_query((vaddr_t)dtoh_buffer, &pa, NULL);
DEBUG_ASSERT(err == NO_ERROR);
buffer_desc.dtoh_buffer_phys = pa;
buffer_desc.dtoh_buffer_len = DCC_BUFLEN;
err = arch_mmu_query((vaddr_t)&buffer_desc, &buffer_desc_phys, NULL);
DEBUG_ASSERT(err == NO_ERROR);
arch_clean_cache_range((vaddr_t)&buffer_desc, sizeof(buffer_desc));
}

66
app/lkboot/inet.c Normal file
View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* 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.
*/
#if WITH_LIB_MINIP
#include <app.h>
#include <platform.h>
#include <stdio.h>
#include <debug.h>
#include <string.h>
#include <pow2.h>
#include <err.h>
#include <assert.h>
#include <trace.h>
#include <app/lkboot.h>
#include "lkboot.h"
#include <lib/minip.h>
#define LOCAL_TRACE 0
static ssize_t tcp_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;
}
lkb_t *lkboot_tcp_opened(void *s)
{
lkb_t *lkb;
lkb = lkboot_create_lkb(s, tcp_readx, (void *)tcp_write);
if (!lkb)
return NULL;
return lkb;
}
#endif

View File

@@ -21,6 +21,8 @@
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "lkboot.h"
#include <app.h>
#include <platform.h>
@@ -38,23 +40,15 @@
#include <kernel/vm.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_WITH_SERVER 1
#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;
#define STATE_OPEN 0
#define STATE_DATA 1
#define STATE_RESP 2
@@ -62,23 +56,27 @@ size_t lkb_iobuffer_size = 16*1024*1024;
#define STATE_ERROR 4
typedef struct LKB {
tcp_socket_t *s;
lkb_read_hook *read;
lkb_write_hook *write;
void *cookie;
int state;
size_t avail;
} lkb_t;
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);
lkb_t *lkboot_create_lkb(void *cookie, lkb_read_hook *read, lkb_write_hook *write) {
lkb_t *lkb = malloc(sizeof(lkb_t));
if (!lkb)
return NULL;
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;
lkb->cookie = cookie;
lkb->state = STATE_OPEN;
lkb->avail = 0;
lkb->read = read;
lkb->write = write;
return lkb;
}
static int lkb_send(lkb_t *lkb, u8 opcode, const void *data, size_t len) {
@@ -116,12 +114,12 @@ static int lkb_send(lkb_t *lkb, u8 opcode, const void *data, size_t len) {
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)) {
if (lkb->write(lkb->cookie, &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)) {
if (len && (lkb->write(lkb->cookie, data, len) != (ssize_t)len)) {
printf("xmit data fail\n");
lkb->state = STATE_ERROR;
return -1;
@@ -146,7 +144,6 @@ int lkb_write(lkb_t *lkb, const void *_data, size_t len) {
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;
}
@@ -156,7 +153,7 @@ int lkb_read(lkb_t *lkb, void *_data, size_t len) {
while (len > 0) {
if (lkb->avail == 0) {
msg_hdr_t hdr;
if (readx(lkb->s, &hdr, sizeof(hdr))) goto fail;
if (lkb->read(lkb->cookie, &hdr, sizeof(hdr))) goto fail;
if (hdr.opcode == MSG_END_DATA) {
lkb->state = STATE_RESP;
return -1;
@@ -165,11 +162,11 @@ int lkb_read(lkb_t *lkb, void *_data, size_t len) {
lkb->avail = ((size_t) hdr.length) + 1;
}
if (lkb->avail >= len) {
if (readx(lkb->s, data, len)) goto fail;
if (lkb->read(lkb->cookie, data, len)) goto fail;
lkb->avail -= len;
return 0;
}
if (readx(lkb->s, data, lkb->avail)) {
if (lkb->read(lkb->cookie, data, lkb->avail)) {
lkb->state = STATE_ERROR;
return -1;
}
@@ -184,120 +181,126 @@ fail:
return -1;
}
static status_t handle_txn(tcp_socket_t *s)
status_t lkboot_process_command(lkb_t *lkb)
{
msg_hdr_t hdr;
lkb_t lkb;
char cmd[128];
char *arg;
int err;
const char *result;
unsigned len;
if (readx(s, &hdr, sizeof(hdr))) goto fail;
if (lkb->read(lkb->cookie, &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;
if (lkb->read(lkb->cookie, cmd, hdr.length)) goto fail;
cmd[hdr.length] = 0;
printf("lkboot: recv '%s'\n", cmd);
TRACEF("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, &result);
err = lkb_handle_command(lkb, cmd, arg, len, &result);
if (err >= 0) {
lkb_okay(&lkb);
lkb_okay(lkb);
} else {
lkb_fail(&lkb, result);
lkb_fail(lkb, result);
}
TRACEF("command handled with success\n");
return NO_ERROR;
fail:
tcp_close(s);
TRACEF("command failed\n");
return ERR_IO;
}
static status_t lkboot_server(bool wait_forever)
{
lkboot_dcc_init();
#if WITH_LIB_MINIP
/* open the server's socket */
tcp_socket_t *listen_socket = NULL;
if (tcp_open_listen(&listen_socket, 1023) < 0) {
printf("lkboot: error opening listen socket\n");
return ERR_NO_MEMORY;
}
#endif
/* run the main lkserver loop */
printf("lkboot: starting server\n");
lk_time_t t = current_time(); /* remember when we started */
for (;;) {
bool handled_command = false;
lkb_t *lkb;
#if WITH_LIB_MINIP
/* wait for a new connection */
lk_time_t timeout = 100;
tcp_socket_t *s;
if (tcp_accept_timeout(listen_socket, &s, timeout) >= 0) {
DEBUG_ASSERT(s);
/* handle the command and close it */
lkb = lkboot_tcp_opened(s);
lkboot_process_command(lkb);
free(lkb);
tcp_close(s);
handled_command = true;
}
#endif
/* check if anything is coming in on dcc */
lkb = lkboot_check_dcc_open();
if (lkb) {
lkboot_process_command(lkb);
free(lkb);
handled_command = true;
}
/* after the first command, stay in the server loop forever */
if (handled_command && !wait_forever) {
wait_forever = true;
printf("lkboot: handled command, staying in server loop\n");
}
/* see if we need to drop out and try to direct boot */
if (!wait_forever) {
if (current_time() - t >= LKBOOT_SERVER_TIMEOUT) {
break;
}
}
}
#if WITH_LIB_MINIP
tcp_close(listen_socket);
#endif
printf("lkboot: server timed out\n");
return ERR_TIMED_OUT;
}
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);
// 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);
serverloop:
/* open the server's socket */
if (tcp_open_listen(&listen_socket, 1023) < 0) {
printf("lkboot: error opening listen socket\n");
goto regboot;
}
/* run the main lkserver loop */
printf("lkboot: starting network server\n");
lk_time_t t = current_time();
for (;;) {
tcp_socket_t *s;
/* 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;
}
/* wait for a new connection */
if (tcp_accept_timeout(listen_socket, &s, timeout) < 0) {
continue;
}
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");
lkboot_server(false);
#endif
regboot:
printf("lkboot: trying to boot from flash...\n");
TRACEF("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;
TRACEF("restarting server\n");
lkboot_server(true);
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 Brian Swetland
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
@@ -22,59 +22,26 @@
*/
#pragma once
typedef struct {
unsigned char opcode;
unsigned char extra;
unsigned short length;
} msg_hdr_t;
#include <sys/types.h>
#include <app/lkboot.h>
#include "lkboot_protocol.h"
// unless otherwise specified, extra is always zero.
/* private to lkboot app */
#define MSG_OKAY 0x00
// length must be zero.
// server indicates command was successful.
int lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, size_t len, const char **result);
#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
status_t do_flash_boot(void);
#define MSG_LOG 0xFE
// data contains human readable log message from server
// server may issue these at any time
typedef ssize_t lkb_read_hook(void *s, void *data, size_t len);
typedef ssize_t lkb_write_hook(void *s, const void *data, size_t len);
#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
lkb_t *lkboot_create_lkb(void *cookie, lkb_read_hook *read, lkb_write_hook *write);
status_t lkboot_process_command(lkb_t *);
#define MSG_CMD 0x40
// length must be greater than zero
// data will contain an ascii command
// server may reject excessively large commands
/* inet server */
lkb_t *lkboot_tcp_opened(void *s);
#define MSG_SEND_DATA 0x41
// client sends data to server
// length is datalen -1 (to allow for full 64k chunks)
/* dcc based server */
void lkboot_dcc_init(void);
lkb_t *lkboot_check_dcc_open(void);
#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

View File

@@ -0,0 +1,80 @@
/*
* 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.
*/
#pragma once
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

53
app/lkboot/pdcc.h Normal file
View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* 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.
*/
#pragma once
#include <stdint.h>
/* in memory and DCC descriptors for the PDCC protocol */
/* shared outside of lk repository, be careful of modifications */
#define PDCC_VERSION 1
struct pdcc_buffer_descriptor {
uint32_t version;
uint32_t htod_buffer_phys;
uint32_t htod_buffer_len;
uint32_t dtoh_buffer_phys;
uint32_t dtoh_buffer_len;
};
#define PDCC_VALID (1<<31)
#define PDCC_OPCODE_SHIFT (24)
#define PDCC_OPCODE(x) (((x) >> PDCC_OPCODE_SHIFT) & 0x7f)
#define PDCC_DATA(x) ((x) & 0x00ffffff);
enum {
PDCC_OP_RESET = 0,
PDCC_OP_BUF_HEADER,
PDCC_OP_UPDATE_OUT_INDEX,
PDCC_OP_CONSUMED_IN,
};

View File

@@ -6,12 +6,15 @@ MODULE_DEPS += \
lib/bio \
lib/bootargs \
lib/bootimage \
lib/cbuf \
lib/ptable \
lib/sysparam
MODULE_SRCS += \
$(LOCAL_DIR)/commands.c \
$(LOCAL_DIR)/dcc.c \
$(LOCAL_DIR)/inet.c \
$(LOCAL_DIR)/lkboot.c \
$(LOCAL_DIR)/commands.c
GLOBAL_INCLUDES += $(LOCAL_DIR)/include