From 3764cb3a2d011fb151a36c4746b8a918b75d6561 Mon Sep 17 00:00:00 2001 From: Travis Geiselbrecht Date: Thu, 23 Apr 2015 19:32:47 -0700 Subject: [PATCH] [app][lkboot] modularlize lkboot, add secondary DCC channel, remove large buffer Change-Id: I5a59f06c909722fe7269aa0991da10fa1ea8196e --- app/lkboot/commands.c | 173 ++++++++++++++++++----- app/lkboot/dcc.c | 259 +++++++++++++++++++++++++++++++++++ app/lkboot/inet.c | 66 +++++++++ app/lkboot/lkboot.c | 211 ++++++++++++++-------------- app/lkboot/lkboot.h | 65 +++------ app/lkboot/lkboot_protocol.h | 80 +++++++++++ app/lkboot/pdcc.h | 53 +++++++ app/lkboot/rules.mk | 5 +- 8 files changed, 724 insertions(+), 188 deletions(-) create mode 100644 app/lkboot/dcc.c create mode 100644 app/lkboot/inet.c create mode 100644 app/lkboot/lkboot_protocol.h create mode 100644 app/lkboot/pdcc.h diff --git a/app/lkboot/commands.c b/app/lkboot/commands.c index 3b94374f..d0a84fa2 100644 --- a/app/lkboot/commands.c +++ b/app/lkboot/commands.c @@ -32,8 +32,10 @@ #include #include #include +#include #include +#include #include #include @@ -45,16 +47,13 @@ #if PLATFORM_ZYNQ #include +#include #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; diff --git a/app/lkboot/dcc.c b/app/lkboot/dcc.c new file mode 100644 index 00000000..999464a5 --- /dev/null +++ b/app/lkboot/dcc.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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)); +} + diff --git a/app/lkboot/inet.c b/app/lkboot/inet.c new file mode 100644 index 00000000..defac381 --- /dev/null +++ b/app/lkboot/inet.c @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "lkboot.h" + +#include + +#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 diff --git a/app/lkboot/lkboot.c b/app/lkboot/lkboot.c index fc3b55f4..a97777ed 100644 --- a/app/lkboot/lkboot.c +++ b/app/lkboot/lkboot.c @@ -21,6 +21,8 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "lkboot.h" + #include #include @@ -38,23 +40,15 @@ #include #include -#include "lkboot.h" - #if WITH_LIB_MINIP #include -#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 diff --git a/app/lkboot/lkboot.h b/app/lkboot/lkboot.h index bc34b090..4d3bb1ca 100644 --- a/app/lkboot/lkboot.h +++ b/app/lkboot/lkboot.h @@ -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 +#include +#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 -// ':' ':' - -// 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 diff --git a/app/lkboot/lkboot_protocol.h b/app/lkboot/lkboot_protocol.h new file mode 100644 index 00000000..bc34b090 --- /dev/null +++ b/app/lkboot/lkboot_protocol.h @@ -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 +// ':' ':' + +// 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 diff --git a/app/lkboot/pdcc.h b/app/lkboot/pdcc.h new file mode 100644 index 00000000..068b878c --- /dev/null +++ b/app/lkboot/pdcc.h @@ -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 + +/* 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, +}; + diff --git a/app/lkboot/rules.mk b/app/lkboot/rules.mk index c4c48dec..911e70c3 100644 --- a/app/lkboot/rules.mk +++ b/app/lkboot/rules.mk @@ -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