1 /*
2  * Copyright (c) 2015 Carlos Pizano-Uribe <cpu@chromium.org>
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 
9 
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdint.h>
13 #include <stdlib.h>
14 
15 #include <lib/tftp.h>
16 #include <lib/cksum.h>
17 #include <lib/elf.h>
18 
19 #include <kernel/thread.h>
20 
21 #include <lk/console_cmd.h>
22 
23 #if defined(SDRAM_BASE)
24 #define DOWNLOAD_BASE ((unsigned char*)SDRAM_BASE)
25 #else
26 #define DOWNLOAD_BASE ((unsigned char*)0)
27 #endif
28 
29 #define FNAME_SIZE 64
30 #define DOWNLOAD_SLOT_SIZE (512 * 1024)
31 
32 typedef enum {
33     DOWNLOAD_ANY,
34     DOWNLOAD_ELF,
35 } download_type;
36 
37 typedef struct {
38     unsigned char *start;
39     unsigned char *end;
40     unsigned char *max;
41     char name[FNAME_SIZE];
42     download_type type;
43 } download_t;
44 
make_download(const char * name)45 static download_t *make_download(const char *name) {
46     download_t *d = malloc(sizeof(download_t));
47     memset(d, 0, sizeof(download_t));
48     strncpy(d->name, name, FNAME_SIZE);
49     return d;
50 }
51 
set_ram_zone(download_t * d,unsigned char * spot,int slot)52 static void set_ram_zone(download_t *d, unsigned char *spot, int slot) {
53     d->start = spot + (DOWNLOAD_SLOT_SIZE * slot);
54     d->end = d->start;
55     d->max = d->end + DOWNLOAD_SLOT_SIZE;
56     memset(spot, 0, DOWNLOAD_SLOT_SIZE);
57 }
58 
output_result(const download_t * download)59 static size_t output_result(const download_t *download) {
60     size_t len = download->end - download->start;
61     unsigned long crc = crc32(0, download->start, len);
62     printf("[%s] done, start at: %p - %zu bytes, crc32 = %lu\n",
63            download->name, download->start, len, crc);
64     return len;
65 }
66 
run_elf(void * entry_point)67 static int run_elf(void *entry_point) {
68     void (*elf_start)(void) = (void *)entry_point;
69     printf("elf (%p) running ...\n", entry_point);
70     thread_sleep(10);
71     elf_start();
72     printf("elf (%p) finished\n", entry_point);
73     return 0;
74 }
75 
process_elf_blob(const void * start,size_t len)76 static void process_elf_blob(const void *start, size_t len) {
77     void *entrypt;
78     elf_handle_t elf;
79 
80     status_t st = elf_open_handle_memory(&elf, start, len);
81     if (st < 0) {
82         printf("unable to open elf handle\n");
83         return;
84     }
85 
86     st = elf_load(&elf);
87     if (st < 0) {
88         printf("elf processing failed, status : %d\n", st);
89         goto exit;
90     }
91 
92     entrypt = (void *)elf.entry;
93     if (entrypt < start || entrypt >= (void *)((char *)start + len)) {
94         printf("out of bounds entrypoint for elf : %p\n", entrypt);
95         goto exit;
96     }
97 
98     printf("elf looks good\n");
99     thread_resume(thread_create("elf_runner", &run_elf, entrypt,
100                                 DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
101 exit:
102     elf_close_handle(&elf);
103 }
104 
tftp_callback(void * data,size_t len,void * arg)105 static int tftp_callback(void *data, size_t len, void *arg) {
106     download_t *download = arg;
107     size_t final_len;
108 
109     if (!data) {
110         final_len = output_result(download);
111         if (download->type == DOWNLOAD_ELF) {
112             process_elf_blob(download->start, final_len);
113         }
114 
115         download->end = download->start;
116         return 0;
117     }
118 
119     if ((download->end + len) > download->max) {
120         printf("transfer too big, aborting\n");
121         return -1;
122     }
123     if (len) {
124         memcpy(download->end, data, len);
125         download->end += len;
126     }
127     return 0;
128 }
129 
loader(int argc,const console_cmd_args * argv)130 static int loader(int argc, const console_cmd_args *argv) {
131     static int any_slot = 0;
132     static int elf_slot = 1;
133 
134     download_t *download;
135     int slot;
136 
137     if (!DOWNLOAD_BASE) {
138         printf("loader not available. it needs sdram\n");
139         return 0;
140     }
141 
142     if (argc < 3) {
143 usage:
144         printf("load any [filename] <slot>\n"
145                "load elf [filename] <slot>\n"
146                "protocol is tftp and <slot> is optional\n");
147         return 0;
148     }
149 
150     download = make_download(argv[2].str);
151 
152     if (strcmp(argv[1].str, "any") == 0) {
153         download->type = DOWNLOAD_ANY;
154         slot = any_slot;
155         any_slot += 2;
156     } else if (strcmp(argv[1].str, "elf") == 0) {
157         download->type = DOWNLOAD_ELF;
158         slot = elf_slot;
159         elf_slot += 2;
160     } else {
161         goto usage;
162     }
163 
164     if (argc == 4) {
165         slot = argv[3].i;
166     }
167 
168     set_ram_zone(download, DOWNLOAD_BASE, slot);
169     tftp_set_write_client(download->name, &tftp_callback, download);
170     printf("ready for %s over tftp (at %p)\n", argv[2].str, download->start);
171     return 0;
172 }
173 
174 STATIC_COMMAND_START
175 STATIC_COMMAND("load", "download and run via tftp", &loader)
176 STATIC_COMMAND_END(loader);
177 
178