| /* |
| * 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); |
| |