blob: c363ee15588fcf40a4fcbe7800da81844f6d8fb5 [file] [log] [blame]
/*
* Copyright (c) 2015 Carlos Pizano-Uribe <cpu@chromium.org>
*
* 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 <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <lib/tftp.h>
#include <lib/cksum.h>
#include <lib/elf.h>
#include <kernel/thread.h>
#if defined(WITH_LIB_CONSOLE)
#include <lib/console.h>
#else
#error "loader app needs a console"
#endif
#if defined(SDRAM_BASE)
#define DOWNLOAD_BASE ((void*)SDRAM_BASE)
#else
#define DOWNLOAD_BASE ((void*)0)
#endif
#define FNAME_SIZE 64
#define DOWNLOAD_SLOT_SIZE (512 * 1024)
typedef enum {
DOWNLOAD_ANY,
DOWNLOAD_ELF,
} download_type;
typedef struct {
unsigned char* start;
unsigned char* end;
unsigned char* max;
char name[FNAME_SIZE];
download_type type;
} download_t;
static download_t* make_download(const char* name)
{
download_t* d = malloc(sizeof(download_t));
memset(d, 0, sizeof(download_t));
strncpy(d->name, name, FNAME_SIZE);
return d;
}
static void set_ram_zone(download_t* d, int slot) {
d->start = DOWNLOAD_BASE + (DOWNLOAD_SLOT_SIZE * slot);
d->end = d->start;
d->max = d->end + DOWNLOAD_SLOT_SIZE;
memset(d->start, 0, DOWNLOAD_SLOT_SIZE);
}
static size_t output_result(const download_t* download)
{
size_t len = download->end - download->start;
unsigned long crc = crc32(0, download->start, len);
printf("[%s] done, start at: %p - %zu bytes, crc32 = %lu\n",
download->name, download->start, len, crc);
return len;
}
static int run_elf(void* entry_point)
{
void (*elf_start)(void) = (void*)entry_point;
printf("elf (%p) running ...\n", entry_point);
thread_sleep(10);
elf_start();
printf("elf (%p) finished\n", entry_point);
return 0;
}
static void process_elf_blob(const void* start, size_t len)
{
void* entrypt;
elf_handle_t elf;
status_t st = elf_open_handle_memory(&elf, start, len);
if (st < 0) {
printf("unable to open elf handle\n");
return;
}
st = elf_load(&elf);
if (st < 0) {
printf("elf processing failed, status : %d\n", st);
goto exit;
}
entrypt = (void*)elf.entry;
if (entrypt < start || entrypt >= (void*)((char*)start + len)) {
printf("out of bounds entrypoint for elf : %p\n", entrypt);
goto exit;
}
printf("elf looks good\n");
thread_resume(thread_create("elf_runner", &run_elf, entrypt,
DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
exit:
elf_close_handle(&elf);
}
int tftp_callback(void* data, size_t len, void* arg)
{
download_t* download = arg;
size_t final_len;
if (!data) {
final_len = output_result(download);
if (download->type == DOWNLOAD_ELF) {
process_elf_blob(download->start, final_len);
}
download->end = download->start;
return 0;
}
if ((download->end + len) > download->max) {
printf("transfer too big, aborting\n");
return -1;
}
if (len) {
memcpy(download->end, data, len);
download->end += len;
}
return 0;
}
static int loader(int argc, const cmd_args *argv)
{
static int any_slot = 0;
static int elf_slot = 1;
download_t* download;
int slot;
if (!DOWNLOAD_BASE) {
printf("loader not available. it needs sdram\n");
return 0;
}
if (argc < 3) {
usage:
printf("load any [filename] <slot>\n"
"load elf [filename] <slot>\n"
"protocol is tftp and <slot> is optional\n");
return 0;
}
download = make_download(argv[2].str);
if (strcmp(argv[1].str, "any") == 0) {
download->type = DOWNLOAD_ANY;
slot = any_slot;
any_slot += 2;
} else if (strcmp(argv[1].str, "elf") == 0) {
download->type = DOWNLOAD_ELF;
slot = elf_slot;
elf_slot += 2;
} else {
goto usage;
}
if (argc == 4) {
slot = argv[3].i;
}
set_ram_zone(download, slot);
tftp_set_write_client(download->name, &tftp_callback, download);
printf("ready for %s over tftp (at %p)\n", argv[2].str, download->start);
return 0;
}
STATIC_COMMAND_START
STATIC_COMMAND("load", "download and run via tftp", &loader)
STATIC_COMMAND_END(loader);