[Feature]add MT2731_MP2_MR2_SVN388 baseline version
Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/bsp/lk/app/lkboot/commands.c b/src/bsp/lk/app/lkboot/commands.c
new file mode 100644
index 0000000..c6c45f3
--- /dev/null
+++ b/src/bsp/lk/app/lkboot/commands.c
@@ -0,0 +1,413 @@
+/*
+ * 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 <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <endian.h>
+#include <malloc.h>
+#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>
+#include <lib/bootimage.h>
+#include <lib/ptable.h>
+#include <lib/sysparam.h>
+
+#include <app/lkboot.h>
+
+#if PLATFORM_ZYNQ
+#include <platform/fpga.h>
+#include <platform/zynq.h>
+#endif
+
+#define bootdevice "spi0"
+
+#define LOCAL_TRACE 0
+
+struct lkb_command {
+ 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;
+ }
+}
+
+static int do_reboot(void *arg) {
+ thread_sleep(250);
+ platform_halt(HALT_ACTION_REBOOT, HALT_REASON_SW_RESET);
+ return 0;
+}
+
+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 */
+ 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);
+
+ const void *ptr;
+
+ /* sniff it to see if it's a bootimage or a raw image */
+ bootimage_t *bi;
+ if (bootimage_open(buf, len, &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, "pmem", buf_phys, bootimage_size);
+ }
+ } else {
+ /* raw image, just chain load it directly */
+ TRACEF("raw image, chainloading\n");
+
+ ptr = buf;
+ }
+
+ /* 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;
+}
+
+/* try to boot the system from a flash partition */
+status_t do_flash_boot(void)
+{
+ status_t err;
+
+ LTRACE_ENTRY;
+
+ /* construct a boot argument list */
+ 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);
+
+ 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
+int lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, size_t len, const char **result)
+{
+ *result = NULL;
+
+ 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 (!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 (ptable_add(arg, plen, 0) < 0) {
+ *result = "error creating partition";
+ return -1;
+ }
+
+ 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 (!(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");
+
+ 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) {
+ *result = "remove failed";
+ return -1;
+ }
+ } else if (!strcmp(cmd, "fpga")) {
+#if PLATFORM_ZYNQ
+ 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(pa, len);
+
+ free(buf);
+#else
+ *result = "no fpga";
+ return -1;
+#endif
+ } else if (!strcmp(cmd, "boot")) {
+ return do_boot(lkb, len, result);
+ } 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;
+ }
+
+ return 0;
+}
diff --git a/src/bsp/lk/app/lkboot/dcc.c b/src/bsp/lk/app/lkboot/dcc.c
new file mode 100644
index 0000000..e12b99b
--- /dev/null
+++ b/src/bsp/lk/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 <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;
+ __UNUSED 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/src/bsp/lk/app/lkboot/include/app/lkboot.h b/src/bsp/lk/app/lkboot/include/app/lkboot.h
new file mode 100644
index 0000000..03f81a1
--- /dev/null
+++ b/src/bsp/lk/app/lkboot/include/app/lkboot.h
@@ -0,0 +1,43 @@
+/*
+ * 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 LKB lkb_t;
+
+// lkb_read/write may *only* be called from within a lkb_handler()
+// len of 0 is invalid for both
+
+// returns 0 on success, -1 on failure (io error, etc)
+int lkb_read(lkb_t *lkb, void *data, size_t len);
+int lkb_write(lkb_t *lkb, const void *data, size_t len);
+
+// len is the number of bytes the host has declared that it will send
+// 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);
+
+// cmd must be a string constant
+void lkb_register(const char *cmd, lkb_handler_t handler, void *cookie);
+
diff --git a/src/bsp/lk/app/lkboot/inet.c b/src/bsp/lk/app/lkboot/inet.c
new file mode 100644
index 0000000..defac38
--- /dev/null
+++ b/src/bsp/lk/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 <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
diff --git a/src/bsp/lk/app/lkboot/lkboot.c b/src/bsp/lk/app/lkboot/lkboot.c
new file mode 100644
index 0000000..a5b432e
--- /dev/null
+++ b/src/bsp/lk/app/lkboot/lkboot.c
@@ -0,0 +1,354 @@
+/*
+ * 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 "lkboot.h"
+
+#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 <lib/sysparam.h>
+
+#include <kernel/thread.h>
+#include <kernel/mutex.h>
+
+#include <kernel/vm.h>
+#include <app/lkboot.h>
+
+#if WITH_LIB_MINIP
+#include <lib/minip.h>
+#endif
+
+#ifndef LKBOOT_WITH_SERVER
+#define LKBOOT_WITH_SERVER 1
+#endif
+#ifndef LKBOOT_AUTOBOOT
+#define LKBOOT_AUTOBOOT 1
+#endif
+#ifndef LKBOOT_AUTOBOOT_TIMEOUT
+#define LKBOOT_AUTOBOOT_TIMEOUT 5000
+#endif
+
+#define LOCAL_TRACE 0
+
+#define STATE_OPEN 0
+#define STATE_DATA 1
+#define STATE_RESP 2
+#define STATE_DONE 3
+#define STATE_ERROR 4
+
+typedef struct LKB {
+ lkb_read_hook *read;
+ lkb_write_hook *write;
+
+ void *cookie;
+
+ int state;
+ size_t avail;
+} lkb_t;
+
+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;
+
+ 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) {
+ msg_hdr_t hdr;
+
+ // 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;
+ }
+
+ hdr.opcode = opcode;
+ hdr.extra = 0;
+ hdr.length = (opcode == MSG_SEND_DATA) ? (len - 1) : len;
+ if (lkb->write(lkb->cookie, &hdr, sizeof(hdr)) != sizeof(&hdr)) {
+ printf("xmit hdr fail\n");
+ lkb->state = STATE_ERROR;
+ return -1;
+ }
+ if (len && (lkb->write(lkb->cookie, 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;
+}
+
+int lkb_read(lkb_t *lkb, void *_data, size_t len) {
+ char *data = _data;
+
+ 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 (lkb->read(lkb->cookie, &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 (lkb->read(lkb->cookie, data, len)) goto fail;
+ lkb->avail -= len;
+ return 0;
+ }
+ if (lkb->read(lkb->cookie, 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;
+}
+
+status_t lkboot_process_command(lkb_t *lkb)
+{
+ msg_hdr_t hdr;
+ char cmd[128];
+ char *arg;
+ int err;
+ const char *result;
+ unsigned len;
+
+ if (lkb->read(lkb->cookie, &hdr, sizeof(hdr))) goto fail;
+ if (hdr.opcode != MSG_CMD) goto fail;
+ if (hdr.length > 127) goto fail;
+ if (lkb->read(lkb->cookie, cmd, hdr.length)) goto fail;
+ cmd[hdr.length] = 0;
+
+ TRACEF("recv '%s'\n", cmd);
+
+ if (!(arg = strchr(cmd, ':'))) goto fail;
+ *arg++ = 0;
+ len = atoul(arg);
+ if (!(arg = strchr(arg, ':'))) goto fail;
+ arg++;
+
+ err = lkb_handle_command(lkb, cmd, arg, len, &result);
+ if (err >= 0) {
+ lkb_okay(lkb);
+ } else {
+ lkb_fail(lkb, result);
+ }
+
+ TRACEF("command handled with success\n");
+ return NO_ERROR;
+
+fail:
+ TRACEF("command failed\n");
+ return ERR_IO;
+}
+
+static status_t lkboot_server(lk_time_t timeout)
+{
+ 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 sock_timeout = 100;
+ tcp_socket_t *s;
+ if (tcp_accept_timeout(listen_socket, &s, sock_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 && timeout != INFINITE_TIME) {
+ timeout = INFINITE_TIME;
+ printf("lkboot: handled command, staying in server loop\n");
+ }
+
+ /* see if we need to drop out and try to direct boot */
+ if (timeout != INFINITE_TIME && (current_time() - t >= timeout)) {
+ break;
+ }
+ }
+
+#if WITH_LIB_MINIP
+ tcp_close(listen_socket);
+#endif
+
+ printf("lkboot: server timed out\n");
+
+ return ERR_TIMED_OUT;
+}
+
+/* platform code can override this to conditionally abort autobooting from flash */
+__WEAK bool platform_abort_autoboot(void)
+{
+ return false;
+}
+
+static void lkboot_task(const struct app_descriptor *app, void *args)
+{
+ /* read a few sysparams to decide if we're going to autoboot */
+ uint8_t autoboot = 1;
+ sysparam_read("lkboot.autoboot", &autoboot, sizeof(autoboot));
+
+ /* let platform code have a shot at disabling the autoboot behavior */
+ if (platform_abort_autoboot())
+ autoboot = 0;
+
+#if !LKBOOT_AUTOBOOT
+ autoboot = 0;
+#endif
+
+ /* if we're going to autoobot, read the timeout value */
+ lk_time_t autoboot_timeout;
+ if (!autoboot) {
+ autoboot_timeout = INFINITE_TIME;
+ } else {
+ autoboot_timeout = LKBOOT_AUTOBOOT_TIMEOUT;
+ sysparam_read("lkboot.autoboot_timeout", &autoboot_timeout, sizeof(autoboot_timeout));
+ }
+
+ TRACEF("autoboot %u autoboot_timeout %u\n", autoboot, (uint)autoboot_timeout);
+
+#if LKBOOT_WITH_SERVER
+ lkboot_server(autoboot_timeout);
+#else
+ if (autoboot_timeout != INFINITE_TIME) {
+ TRACEF("waiting for %u milliseconds before autobooting\n", (uint)autoboot_timeout);
+ thread_sleep(autoboot_timeout);
+ }
+#endif
+
+ if (autoboot_timeout != INFINITE_TIME) {
+ 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
+ TRACEF("restarting server\n");
+ lkboot_server(INFINITE_TIME);
+#endif
+
+ TRACEF("nothing to do, exiting\n");
+}
+
+APP_START(lkboot)
+ .entry = lkboot_task,
+ .flags = 0,
+APP_END
diff --git a/src/bsp/lk/app/lkboot/lkboot.h b/src/bsp/lk/app/lkboot/lkboot.h
new file mode 100644
index 0000000..4d3bb1c
--- /dev/null
+++ b/src/bsp/lk/app/lkboot/lkboot.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <sys/types.h>
+#include <app/lkboot.h>
+#include "lkboot_protocol.h"
+
+/* private to lkboot app */
+
+int lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, size_t len, const char **result);
+
+status_t do_flash_boot(void);
+
+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);
+
+lkb_t *lkboot_create_lkb(void *cookie, lkb_read_hook *read, lkb_write_hook *write);
+status_t lkboot_process_command(lkb_t *);
+
+/* inet server */
+lkb_t *lkboot_tcp_opened(void *s);
+
+/* dcc based server */
+void lkboot_dcc_init(void);
+lkb_t *lkboot_check_dcc_open(void);
+
diff --git a/src/bsp/lk/app/lkboot/lkboot_protocol.h b/src/bsp/lk/app/lkboot/lkboot_protocol.h
new file mode 100644
index 0000000..bc34b09
--- /dev/null
+++ b/src/bsp/lk/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
+// <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
diff --git a/src/bsp/lk/app/lkboot/pdcc.h b/src/bsp/lk/app/lkboot/pdcc.h
new file mode 100644
index 0000000..068b878
--- /dev/null
+++ b/src/bsp/lk/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 <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,
+};
+
diff --git a/src/bsp/lk/app/lkboot/rules.mk b/src/bsp/lk/app/lkboot/rules.mk
new file mode 100644
index 0000000..1ee1376
--- /dev/null
+++ b/src/bsp/lk/app/lkboot/rules.mk
@@ -0,0 +1,19 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+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 \
+
+include make/module.mk