[dartuino][bootloader] Get Bootloader USB working.
This commit is contained in:
32
app/moot/include/app/moot/moot.h
Normal file
32
app/moot/include/app/moot/moot.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Gurjant Kalsi <me@gurjantkalsi.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef APP_MOOT_MOOT_H_
|
||||
#define APP_MOOT_MOOT_H_
|
||||
|
||||
typedef enum {
|
||||
BOOT_NOW,
|
||||
NEXT_BOOT_STRATEGY
|
||||
} next_boot_action_t;
|
||||
|
||||
#endif // APP_MOOT_MOOT_H_
|
||||
50
app/moot/include/app/moot/stubs.h
Normal file
50
app/moot/include/app/moot/stubs.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Gurjant Kalsi <me@gurjantkalsi.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef APP_MOOT_STUBS_H_
|
||||
#define APP_MOOT_STUBS_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef struct moot_sysinfo {
|
||||
uintptr_t sys_base_addr; // Pointer to the base of the main system image.
|
||||
|
||||
size_t btldr_offset;
|
||||
size_t bootloader_len;
|
||||
|
||||
size_t system_offset;
|
||||
size_t system_len;
|
||||
|
||||
char *system_flash_name;
|
||||
} moot_sysinfo_t;
|
||||
|
||||
// Must be implemented by the platform;
|
||||
extern const moot_sysinfo_t moot_system_info;
|
||||
|
||||
// Returns NO_ERROR if it was successfully able to mount a secondary flash
|
||||
// device. If NO_ERROR is returned, mount_path and device_name should also be
|
||||
// populated to reflect the path at which the FS was mounted and the name of
|
||||
// the BIO device that hosts the FS.
|
||||
status_t moot_mount_default_fs(char **mount_path, char **device_name);
|
||||
|
||||
#endif // APP_MOOT_STUBS_H_
|
||||
35
app/moot/include/app/moot/usbboot.h
Normal file
35
app/moot/include/app/moot/usbboot.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Gurjant Kalsi <me@gurjantkalsi.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef APP_MOOT_USB_H_
|
||||
#define APP_MOOT_USB_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// Initialize the USB stack / USB boot mechanisms.
|
||||
void init_usb_boot(void);
|
||||
|
||||
// Allow the USB device to interrupt the boot sequence.
|
||||
void attempt_usb_boot(void);
|
||||
|
||||
#endif // APP_MOOT_USB_H_
|
||||
68
app/moot/moot.c
Normal file
68
app/moot/moot.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Gurjant Kalsi <me@gurjantkalsi.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <app/moot/fsboot.h>
|
||||
#include <app/moot/moot.h>
|
||||
#include <app/moot/stubs.h>
|
||||
#include <app/moot/usbboot.h>
|
||||
|
||||
#include <app.h>
|
||||
#include <arch.h>
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
#include <err.h>
|
||||
#include <kernel/event.h>
|
||||
#include <lk/init.h>
|
||||
#include <stdlib.h>
|
||||
#include <trace.h>
|
||||
|
||||
static void do_boot(void)
|
||||
{
|
||||
arch_disable_ints();
|
||||
arch_quiesce();
|
||||
arch_chain_load((void *)(moot_system_info.sys_base_addr), 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
static void moot_init(const struct app_descriptor *app)
|
||||
{
|
||||
// Initialize our boot subsystems.
|
||||
init_usb_boot();
|
||||
|
||||
}
|
||||
|
||||
static void moot_entry(const struct app_descriptor *app, void *args)
|
||||
{
|
||||
// Wait a few seconds for the host to try to talk to us over USB.
|
||||
attempt_usb_boot();
|
||||
|
||||
// Check the SPIFlash for an upgrade image.
|
||||
attempt_fs_boot();
|
||||
|
||||
// Boot the main system image.
|
||||
do_boot();
|
||||
}
|
||||
|
||||
APP_START(moot)
|
||||
.init = moot_init,
|
||||
.entry = moot_entry,
|
||||
APP_END
|
||||
15
app/moot/rules.mk
Normal file
15
app/moot/rules.mk
Normal file
@@ -0,0 +1,15 @@
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
MODULE := $(LOCAL_DIR)
|
||||
|
||||
MODULE_SRCS += \
|
||||
$(LOCAL_DIR)/fsboot.c \
|
||||
$(LOCAL_DIR)/moot.c \
|
||||
$(LOCAL_DIR)/stubs.c \
|
||||
$(LOCAL_DIR)/usbboot.c \
|
||||
|
||||
|
||||
MODULE_DEPS += \
|
||||
lib/bootimage
|
||||
|
||||
include make/module.mk
|
||||
34
app/moot/stubs.c
Normal file
34
app/moot/stubs.c
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Gurjant Kalsi <me@gurjantkalsi.com>
|
||||
*
|
||||
* 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 <compiler.h>
|
||||
#include <app/moot/stubs.h>
|
||||
#include <err.h>
|
||||
|
||||
// Fail by default. System must override this.
|
||||
__WEAK status_t moot_mount_default_fs(char **mount_path, char **device_name)
|
||||
{
|
||||
*device_name = NULL;
|
||||
*mount_path = NULL;
|
||||
return ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
344
app/moot/usbboot.c
Normal file
344
app/moot/usbboot.c
Normal file
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Gurjant Kalsi <me@gurjantkalsi.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <app/moot/usbboot.h>
|
||||
|
||||
#include <app/moot/stubs.h>
|
||||
#include <dev/udc.h>
|
||||
#include <dev/usb.h>
|
||||
#include <dev/usbc.h>
|
||||
#include <err.h>
|
||||
#include <kernel/event.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <lib/bio.h>
|
||||
#include <lib/buildsig.h>
|
||||
#include <lib/version.h>
|
||||
#include <platform.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <trace.h>
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
#define COMMAND_MAGIC (0x4d4f4f54) // MOOT
|
||||
#define RESP_MAGIC (0x52455350) // RESP
|
||||
|
||||
#define USB_XFER_SIZE (512)
|
||||
#define W(w) (w & 0xff), (w >> 8)
|
||||
#define W3(w) (w & 0xff), ((w >> 8) & 0xff), ((w >> 16) & 0xff)
|
||||
|
||||
// How long should we wait for activity on USB before we continue to boot?
|
||||
#define USB_BOOT_TIMEOUT (3000)
|
||||
|
||||
static const uint8_t if_descriptor[] = {
|
||||
0x09, /* length */
|
||||
INTERFACE, /* type */
|
||||
0x01, /* interface num */ // TODO(gkalsi)
|
||||
0x00, /* alternates */
|
||||
0x02, /* endpoint count */
|
||||
0xff, /* interface class */
|
||||
0x01, /* interface subclass */
|
||||
0x00, /* interface protocol */
|
||||
0x00, /* string index */
|
||||
|
||||
/* endpoint 1 IN */
|
||||
0x07, /* length */
|
||||
ENDPOINT, /* type */
|
||||
0x1 | 0x80, /* address: 1 IN */ // TODO(gkalsi)
|
||||
0x02, /* type: bulk */
|
||||
W(64), /* max packet size: 64 */
|
||||
00, /* interval */
|
||||
|
||||
/* endpoint 1 OUT */
|
||||
0x07, /* length */
|
||||
ENDPOINT, /* type */
|
||||
0x1, /* address: 1 OUT */ // TODO(gkalsi)
|
||||
0x02, /* type: bulk */
|
||||
W(64), /* max packet size: 64 */
|
||||
00, /* interval */
|
||||
};
|
||||
|
||||
// Everything is okay.
|
||||
#define USB_RESP_NO_ERROR (0x00)
|
||||
#define USB_RESP_XMIT_READY (0x01)
|
||||
#define USB_RESP_RECV_READY (0x02)
|
||||
|
||||
// Malformed reqeust
|
||||
#define USB_RESP_BAD_DATA_LEN (0xAAA0)
|
||||
#define USB_RESP_BAD_MAGIC (0xAAA1)
|
||||
#define USB_RESP_UNKNOWN_COMMAND (0xAAA2)
|
||||
|
||||
// Device Side System Errors
|
||||
#define USB_RESP_SYS_IMAGE_TOO_BIG (0xFFF1)
|
||||
#define USB_RESP_ERR_OPEN_SYS_FLASH (0xFFF2)
|
||||
#define USB_RESP_ERR_ERASE_SYS_FLASH (0xFFF3)
|
||||
#define USB_RESP_ERR_WRITE_SYS_FLASH (0xFFF4)
|
||||
#define USB_RESP_CANT_FIND_BUILDSIG (0xFFF5)
|
||||
|
||||
/* Bootloader commands */
|
||||
#define USB_CMD_FLASH (0x01)
|
||||
#define USB_CMD_BOOT (0x02)
|
||||
#define USB_CMD_DEVINFO (0x03)
|
||||
|
||||
typedef struct cmd_header {
|
||||
uint32_t magic;
|
||||
uint32_t cmd;
|
||||
uint32_t arg;
|
||||
} cmd_header_t;
|
||||
|
||||
typedef struct cmd_response {
|
||||
uint32_t magic;
|
||||
uint32_t code;
|
||||
uint32_t arg;
|
||||
} cmd_response_t;
|
||||
|
||||
// USB Functions
|
||||
static void usb_xmit(void *data, size_t len);
|
||||
static status_t usb_recv(void *data, size_t len, lk_time_t timeout, size_t *actual);
|
||||
static status_t usb_xmit_cplt_cb(ep_t endpoint, usbc_transfer_t *t);
|
||||
static status_t usb_recv_cplt_cb(ep_t endpoint, usbc_transfer_t *t);
|
||||
static status_t usb_register_cb(
|
||||
void *cookie, usb_callback_op_t op, const union usb_callback_args *args);
|
||||
|
||||
static uint8_t buffer[4096];
|
||||
static event_t txevt = EVENT_INITIAL_VALUE(txevt, 0, EVENT_FLAG_AUTOUNSIGNAL);
|
||||
static event_t rxevt = EVENT_INITIAL_VALUE(rxevt, 0, EVENT_FLAG_AUTOUNSIGNAL);
|
||||
static volatile bool online = false;
|
||||
|
||||
// Command processor that handles USB boot commands.
|
||||
static bool handle(const void *data, const size_t n, cmd_response_t *resp)
|
||||
{
|
||||
DEBUG_ASSERT(resp);
|
||||
|
||||
resp->magic = RESP_MAGIC;
|
||||
resp->code = USB_RESP_NO_ERROR;
|
||||
resp->arg = 0;
|
||||
|
||||
// Make sure we have enough data.
|
||||
if (n < sizeof(cmd_header_t)) {
|
||||
resp->code = USB_RESP_BAD_DATA_LEN;
|
||||
return false;
|
||||
}
|
||||
|
||||
cmd_header_t *header = (cmd_header_t *)data;
|
||||
if (header->magic != COMMAND_MAGIC) {
|
||||
resp->code = USB_RESP_BAD_MAGIC;
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t image_length;
|
||||
const lk_version_t *version;
|
||||
status_t st;
|
||||
switch (header->cmd) {
|
||||
case USB_CMD_FLASH:
|
||||
image_length = header->arg;
|
||||
if (image_length > (ssize_t)moot_system_info.system_len) {
|
||||
resp->code = USB_RESP_SYS_IMAGE_TOO_BIG;
|
||||
break;
|
||||
}
|
||||
|
||||
// Make space on the flash for the data.
|
||||
bdev_t *dev = bio_open(moot_system_info.system_flash_name);
|
||||
if (!dev) {
|
||||
resp->code = USB_RESP_ERR_OPEN_SYS_FLASH;
|
||||
break;
|
||||
}
|
||||
|
||||
ssize_t n_bytes_erased = bio_erase(dev, moot_system_info.system_offset, image_length);
|
||||
if (n_bytes_erased < image_length) {
|
||||
resp->code = USB_RESP_ERR_ERASE_SYS_FLASH;
|
||||
break;
|
||||
}
|
||||
|
||||
// Signal to the host to start sending the image over.
|
||||
resp->code = USB_RESP_RECV_READY;
|
||||
resp->arg = 0;
|
||||
usb_xmit((void *)resp, sizeof(*resp));
|
||||
|
||||
off_t addr = moot_system_info.system_offset;
|
||||
while (image_length > 0) {
|
||||
ssize_t xfer = (image_length > (ssize_t)sizeof(buffer)) ?
|
||||
(ssize_t)sizeof(buffer) : image_length;
|
||||
|
||||
size_t bytes_received;
|
||||
usb_recv(buffer, xfer, INFINITE_TIME, &bytes_received);
|
||||
|
||||
ssize_t written = bio_write(dev, buffer, addr, bytes_received);
|
||||
if (written != (ssize_t)bytes_received) {
|
||||
resp->code = USB_RESP_ERR_WRITE_SYS_FLASH;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
addr += written;
|
||||
image_length -= written;
|
||||
}
|
||||
|
||||
resp->code = USB_RESP_NO_ERROR;
|
||||
|
||||
break;
|
||||
case USB_CMD_BOOT:
|
||||
resp->code = USB_RESP_NO_ERROR;
|
||||
resp->arg = 0;
|
||||
return true;
|
||||
break;
|
||||
case USB_CMD_DEVINFO:
|
||||
st = buildsig_search(
|
||||
(void *)moot_system_info.sys_base_addr,
|
||||
DEFAULT_BUILDSIG_SEARCH_LEN,
|
||||
1024*1024,
|
||||
&version
|
||||
);
|
||||
if (st != NO_ERROR) {
|
||||
resp->code = USB_RESP_CANT_FIND_BUILDSIG;
|
||||
break;
|
||||
}
|
||||
|
||||
size_t buflen = sizeof(buffer);
|
||||
snprintf((char *)buffer, buflen, "%s\n%s\n%s\n%s\n%s",
|
||||
version->arch, version->platform, version->target,
|
||||
version->project, version->buildid);
|
||||
|
||||
resp->code = USB_RESP_XMIT_READY;
|
||||
resp->arg = strnlen((char *)buffer, buflen);
|
||||
usb_xmit((void *)resp, sizeof(*resp));
|
||||
usb_xmit((void *)buffer, resp->arg);
|
||||
|
||||
resp->arg = 0;
|
||||
resp->code = USB_RESP_NO_ERROR;
|
||||
break;
|
||||
default:
|
||||
resp->arg = 0;
|
||||
resp->code = USB_RESP_UNKNOWN_COMMAND;
|
||||
break;
|
||||
}
|
||||
|
||||
finish:
|
||||
return false;
|
||||
}
|
||||
|
||||
void init_usb_boot(void)
|
||||
{
|
||||
usb_append_interface_lowspeed(if_descriptor, sizeof(if_descriptor));
|
||||
usb_append_interface_highspeed(if_descriptor, sizeof(if_descriptor));
|
||||
|
||||
usb_register_callback(&usb_register_cb, NULL);
|
||||
}
|
||||
|
||||
void attempt_usb_boot(void)
|
||||
{
|
||||
printf("attempting usb boot\n");
|
||||
uint8_t *buf = malloc(USB_XFER_SIZE);
|
||||
|
||||
lk_time_t start = current_time();
|
||||
lk_time_t timeout = USB_BOOT_TIMEOUT;
|
||||
size_t bytes_received;
|
||||
|
||||
while (current_time() - start < timeout) {
|
||||
if (!online) {
|
||||
thread_yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
status_t r = usb_recv(buf, USB_XFER_SIZE, timeout, &bytes_received);
|
||||
if (r == ERR_TIMED_OUT) {
|
||||
goto finish;
|
||||
} else if (r == NO_ERROR) {
|
||||
// Somebody tried to talk to us over USB, they own the boot now.
|
||||
cmd_response_t response;
|
||||
bool should_boot = handle(buf, bytes_received, &response);
|
||||
usb_xmit((void *)&response, sizeof(response));
|
||||
timeout = INFINITE_TIME;
|
||||
if (should_boot) {
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
free(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
static status_t usb_register_cb(
|
||||
void *cookie,
|
||||
usb_callback_op_t op,
|
||||
const union usb_callback_args *args
|
||||
)
|
||||
{
|
||||
if (op == USB_CB_ONLINE) {
|
||||
usbc_setup_endpoint(1, USB_IN, 0x40);
|
||||
usbc_setup_endpoint(1, USB_OUT, 0x40);
|
||||
online = true;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
static status_t usb_xmit_cplt_cb(ep_t endpoint, usbc_transfer_t *t)
|
||||
{
|
||||
event_signal(&txevt, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static status_t usb_recv_cplt_cb(ep_t endpoint, usbc_transfer_t *t)
|
||||
{
|
||||
event_signal(&rxevt, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_xmit(void *data, size_t len)
|
||||
{
|
||||
usbc_transfer_t transfer = {
|
||||
.callback = &usb_xmit_cplt_cb,
|
||||
.result = 0,
|
||||
.buf = data,
|
||||
.buflen = len,
|
||||
.bufpos = 0,
|
||||
.extra = 0,
|
||||
};
|
||||
|
||||
usbc_queue_tx(1, &transfer);
|
||||
event_wait(&txevt);
|
||||
}
|
||||
|
||||
static status_t usb_recv(void *data, size_t len, lk_time_t timeout, size_t *actual)
|
||||
{
|
||||
usbc_transfer_t transfer = {
|
||||
.callback = &usb_recv_cplt_cb,
|
||||
.result = 0,
|
||||
.buf = data,
|
||||
.buflen = len,
|
||||
.bufpos = 0,
|
||||
.extra = 0,
|
||||
};
|
||||
|
||||
usbc_queue_rx(1, &transfer);
|
||||
status_t res = event_wait_timeout(&rxevt, timeout);
|
||||
|
||||
if (res != NO_ERROR) {
|
||||
// TODO(gkalsi): Cancel the USB txn?
|
||||
return res;
|
||||
}
|
||||
|
||||
*actual = transfer.bufpos;
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = %ROMBASE%;
|
||||
__rom_start = .;
|
||||
|
||||
/* text/read-only data */
|
||||
.text : {
|
||||
@@ -61,6 +62,7 @@ SECTIONS
|
||||
.dummy_post_rodata : {
|
||||
/* end of rodata, start of data area */
|
||||
__rodata_end = . ;
|
||||
__rom_end = . ;
|
||||
__data_start_rom = .;
|
||||
}
|
||||
|
||||
|
||||
49
target/dartuinoP0/bootloader/bootloader_stubs.c
Normal file
49
target/dartuinoP0/bootloader/bootloader_stubs.c
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Gurjant Kalsi <me@gurjantkalsi.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
static char bootloader_primary_flash_name[] = "flash0";
|
||||
static char bootloader_secondary_flash_name[] = "qspi-flash";
|
||||
static char bootloader_mount_point[] = "/spifs";
|
||||
|
||||
#include <lib/fs.h>
|
||||
#include <err.h>
|
||||
#include <app/moot/stubs.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define BOOTLOADER_LENGTH_KB (128)
|
||||
|
||||
status_t moot_mount_default_fs(char **mount_path, char **device_name)
|
||||
{
|
||||
*mount_path = bootloader_mount_point;
|
||||
*device_name = bootloader_secondary_flash_name;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
const moot_sysinfo_t moot_system_info = {
|
||||
.sys_base_addr = 0x00220000,
|
||||
.btldr_offset = 0x0,
|
||||
.bootloader_len = 1024 * BOOTLOADER_LENGTH_KB,
|
||||
.system_offset = 1024 * BOOTLOADER_LENGTH_KB,
|
||||
.system_len = (1024 * (1024 - BOOTLOADER_LENGTH_KB)),
|
||||
.system_flash_name = bootloader_primary_flash_name,
|
||||
};
|
||||
12
target/dartuinoP0/bootloader/rules.mk
Normal file
12
target/dartuinoP0/bootloader/rules.mk
Normal file
@@ -0,0 +1,12 @@
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
MODULE := $(LOCAL_DIR)
|
||||
|
||||
MODULE_SRCS += \
|
||||
$(LOCAL_DIR)/bootloader_stubs.c
|
||||
|
||||
|
||||
MODULE_DEPS += \
|
||||
lib/fs
|
||||
|
||||
include make/module.mk
|
||||
314
tools/moot/mtldr.py
Normal file
314
tools/moot/mtldr.py
Normal file
@@ -0,0 +1,314 @@
|
||||
"""
|
||||
Copyright (c) 2016 Gurjant Kalsi <me@gurjantkalsi.com>
|
||||
|
||||
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.
|
||||
"""
|
||||
import argparse
|
||||
import binascii
|
||||
import logging
|
||||
import struct
|
||||
import time
|
||||
|
||||
import usb.core
|
||||
import usb.util
|
||||
|
||||
|
||||
class Command:
|
||||
flash = 0x01
|
||||
boot = 0x02
|
||||
devinfo = 0x03
|
||||
|
||||
|
||||
class DataPhaseType:
|
||||
none = 0
|
||||
host_to_device = 1
|
||||
device_to_host = 2
|
||||
|
||||
|
||||
class Retcode:
|
||||
# Normal operation
|
||||
no_error = (0x00)
|
||||
xmit_ready = (0x01)
|
||||
recv_ready = (0x02)
|
||||
|
||||
# Malformed reqeust
|
||||
bad_data_len = (0xAAA0)
|
||||
bad_magic = (0xAAA1)
|
||||
unknown_command = (0xAAA2)
|
||||
|
||||
# Device side system error.
|
||||
sys_image_too_big = (0xFFF1)
|
||||
err_open_sys_flash = (0xFFF2)
|
||||
err_erase_sys_flash = (0xFFF3)
|
||||
err_write_sys_flash = (0xFFF4)
|
||||
cant_find_buildsig = (0xFFF5)
|
||||
|
||||
|
||||
class CommandParam:
|
||||
def __init__(self, data_phase_type):
|
||||
self.data_phase_type = data_phase_type
|
||||
|
||||
VENDOR_ID = 0x9999
|
||||
PRODUCT_ID = 0x9999
|
||||
CLASS_VENDOR_SPECIFIC = 0xFF
|
||||
SUBCLASS_MTLDR_DEBUG = 0x01
|
||||
|
||||
# create logger
|
||||
logger = logging.getLogger('mtldr')
|
||||
logger.setLevel(logging.WARN)
|
||||
|
||||
|
||||
class FindByDeviceClass(object):
|
||||
# Callable object that selects a USB device by a Sub/Device class pair
|
||||
|
||||
def __init__(self, device, subdevice):
|
||||
self._device_class = device
|
||||
self._subdevice_class = subdevice
|
||||
|
||||
def __call__(self, device):
|
||||
for cfg in device:
|
||||
intf = usb.util.find_descriptor(
|
||||
cfg, bInterfaceClass=self._device_class,
|
||||
bInterfaceSubClass=self._subdevice_class)
|
||||
if intf:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class CommandDispatcher:
|
||||
header_struct = struct.Struct("< i i i")
|
||||
header_struct_len = 12 # bytes
|
||||
|
||||
response_struct = struct.Struct("< i i i")
|
||||
response_struct_len = 12 # bytes
|
||||
|
||||
cmd_magic = 0x4d4f4f54
|
||||
resp_magic = 0x52455350
|
||||
|
||||
data_phase_timeout = 10000 # ms
|
||||
|
||||
CommandParams = {
|
||||
Command.flash: CommandParam(DataPhaseType.host_to_device),
|
||||
Command.boot: CommandParam(DataPhaseType.none),
|
||||
Command.devinfo: CommandParam(DataPhaseType.device_to_host),
|
||||
}
|
||||
|
||||
def __init__(self, ep_in, ep_out):
|
||||
self._ep_in = ep_in
|
||||
self._ep_out = ep_out
|
||||
|
||||
def __read_response(self, timeout=None):
|
||||
"""
|
||||
reads a standard response from the USB device.
|
||||
"""
|
||||
if timeout:
|
||||
resp = self._ep_in.read(CommandDispatcher.response_struct_len, timeout=timeout).tostring()
|
||||
else:
|
||||
resp = self._ep_in.read(CommandDispatcher.response_struct_len).tostring()
|
||||
|
||||
logger.debug(
|
||||
("Read %d bytes: " % CommandDispatcher.response_struct_len) +
|
||||
str(binascii.hexlify(resp))
|
||||
)
|
||||
|
||||
resp = CommandDispatcher.response_struct.unpack(resp)
|
||||
if resp[0] != CommandDispatcher.resp_magic:
|
||||
raise("Device responded with an unexpected magic value.")
|
||||
|
||||
logger.debug(
|
||||
("Read Response - retcode = %d, nbytes = %d" % (resp[1], resp[2]))
|
||||
)
|
||||
|
||||
return (resp[1], int(resp[2]))
|
||||
|
||||
def __dispatch_no_data(self, command):
|
||||
"""
|
||||
Dispatches a command that has no data phase.
|
||||
"""
|
||||
logger.debug("Write %d bytes, command = %d" % (0, command))
|
||||
command = CommandDispatcher.header_struct.pack(CommandDispatcher.cmd_magic, command, 0)
|
||||
self._ep_out.write(command)
|
||||
|
||||
retcode, datalen = self.__read_response()
|
||||
assert datalen == 0 # A command with no data can't have a datalen
|
||||
|
||||
return (retcode, list())
|
||||
|
||||
def __dispatch_device_to_host(self, command):
|
||||
"""
|
||||
Dispatches a command that has a device to host data phase.
|
||||
"""
|
||||
logger.debug("Write %d bytes, command = %d" % (0, command))
|
||||
command = CommandDispatcher.header_struct.pack(CommandDispatcher.cmd_magic, command, 0)
|
||||
self._ep_out.write(command)
|
||||
|
||||
retcode, datalen = self.__read_response()
|
||||
if retcode != Retcode.xmit_ready:
|
||||
return (retcode, list())
|
||||
|
||||
logger.debug("Read %d bytes, retcode = %d" % (int(datalen), retcode))
|
||||
resp = self._ep_in.read(int(datalen), timeout=CommandDispatcher.data_phase_timeout)
|
||||
|
||||
retcode, datalen = self.__read_response()
|
||||
|
||||
return (retcode, resp)
|
||||
|
||||
def __dispatch_host_to_device(self, command, data):
|
||||
"""
|
||||
Dispatches a command that has a host to device data phase.
|
||||
"""
|
||||
logger.debug("Write %d bytes, command = %d" % (len(data), command))
|
||||
# Tell the device that we're about to send it data. Also mention how
|
||||
# much data we're about to send.
|
||||
command = CommandDispatcher.header_struct.pack(CommandDispatcher.cmd_magic, command, len(data))
|
||||
self._ep_out.write(command)
|
||||
|
||||
# The device will signal back to us that it's ready to read data.
|
||||
retcode, datalen = self.__read_response(CommandDispatcher.data_phase_timeout)
|
||||
assert datalen == 0
|
||||
|
||||
if retcode != Retcode.recv_ready:
|
||||
# The device experienced an error and is not ready to receive data.
|
||||
return (retcode, list())
|
||||
|
||||
# Write the data back to the device.
|
||||
self._ep_out.write(data, timeout=CommandDispatcher.data_phase_timeout)
|
||||
|
||||
# The device will reply to us to let us know whether or not it received
|
||||
# our data correctly.
|
||||
retcode, datalen = self.__read_response()
|
||||
return (retcode, list())
|
||||
|
||||
def dispatch(self, command, data=None):
|
||||
"""
|
||||
Dispatches a command to the connected USB device.
|
||||
|
||||
A command is composed of a command phase followed by an optional data
|
||||
phase. If the data parameter is specified, dispatch(...) will also
|
||||
attempt to send data to the device.
|
||||
|
||||
Returns a 2-Tuple as follows: (command result, optional data). If the
|
||||
device returned data during the data phase, optional data will contain
|
||||
that data, otherwise it will be None.
|
||||
"""
|
||||
# Make sure the command actually exists.
|
||||
params = CommandDispatcher.CommandParams.get(command)
|
||||
if not params:
|
||||
raise("Command " + str(command) + " does not exist.")
|
||||
|
||||
if params.data_phase_type == DataPhaseType.none:
|
||||
result = self.__dispatch_no_data(command)
|
||||
elif params.data_phase_type == DataPhaseType.host_to_device:
|
||||
result = self.__dispatch_host_to_device(command, data)
|
||||
elif params.data_phase_type == DataPhaseType.device_to_host:
|
||||
result = self.__dispatch_device_to_host(command)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def cmd_devinfo(dispatcher, args):
|
||||
retcode, data = dispatcher.dispatch(Command.devinfo)
|
||||
|
||||
if retcode != Retcode.no_error:
|
||||
print ("Error %d while reading devinfo" % retcode)
|
||||
else:
|
||||
print data.tostring()
|
||||
|
||||
|
||||
def cmd_flash(dispatcher, args):
|
||||
with open(args.bin, 'rb') as file:
|
||||
binary = file.read()
|
||||
|
||||
retcode, data = dispatcher.dispatch(Command.flash, binary)
|
||||
if retcode != Retcode.no_error:
|
||||
print ("Error %d while flashing device" % retcode)
|
||||
|
||||
|
||||
|
||||
def cmd_boot(dispatcher, args):
|
||||
retcode, data = dispatcher.dispatch(Command.boot)
|
||||
if retcode != Retcode.no_error:
|
||||
print ("Error %d while booting device" % retcode)
|
||||
|
||||
|
||||
def main():
|
||||
# Setup the Logger
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
ch.setFormatter(formatter)
|
||||
logger.addHandler(ch)
|
||||
|
||||
# Setup the argument parser
|
||||
parser = argparse.ArgumentParser(prog='PROG')
|
||||
subparsers = parser.add_subparsers(help='sub-command help')
|
||||
|
||||
devinfo_parser = subparsers.add_parser('devinfo', help='a help')
|
||||
devinfo_parser.set_defaults(func=cmd_devinfo)
|
||||
|
||||
flash_parser = subparsers.add_parser('flash', help='b help')
|
||||
flash_parser.add_argument('bin', help="Path to the LK Binary to flash")
|
||||
flash_parser.set_defaults(func=cmd_flash)
|
||||
|
||||
boot_parser = subparsers.add_parser('boot', help='b help')
|
||||
boot_parser.set_defaults(func=cmd_boot)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
logger.info("Waiting for device (vid=0x%04x, pid=0x%04x, "
|
||||
"class=0x%02x, subclass=0x%02x)" %
|
||||
(VENDOR_ID, PRODUCT_ID, CLASS_VENDOR_SPECIFIC,
|
||||
SUBCLASS_MTLDR_DEBUG))
|
||||
while True:
|
||||
dev = usb.core.find(
|
||||
idVendor=VENDOR_ID,
|
||||
idProduct=PRODUCT_ID,
|
||||
custom_match=FindByDeviceClass(CLASS_VENDOR_SPECIFIC,
|
||||
SUBCLASS_MTLDR_DEBUG))
|
||||
if dev:
|
||||
break
|
||||
time.sleep(0.5)
|
||||
|
||||
logger.info("Found USB Device!")
|
||||
dev.set_configuration()
|
||||
|
||||
cfg = dev.get_active_configuration()
|
||||
intf = usb.util.find_descriptor(
|
||||
cfg, bInterfaceClass=CLASS_VENDOR_SPECIFIC,
|
||||
bInterfaceSubClass=SUBCLASS_MTLDR_DEBUG)
|
||||
|
||||
ep_out = usb.util.find_descriptor(
|
||||
intf,
|
||||
custom_match=lambda e:
|
||||
usb.util.endpoint_direction(e.bEndpointAddress) ==
|
||||
usb.util.ENDPOINT_OUT)
|
||||
ep_in = usb.util.find_descriptor(
|
||||
intf,
|
||||
custom_match=lambda e:
|
||||
usb.util.endpoint_direction(e.bEndpointAddress) ==
|
||||
usb.util.ENDPOINT_IN)
|
||||
|
||||
dispatcher = CommandDispatcher(ep_in, ep_out)
|
||||
args.func(dispatcher, args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user