blob: c6c45f39c1d75ae1aef1171c87930c2a9dd1de0f [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2014 Brian Swetland
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24
25#include <platform.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <debug.h>
29#include <string.h>
30#include <endian.h>
31#include <malloc.h>
32#include <arch.h>
33#include <err.h>
34#include <trace.h>
35#include <pow2.h>
36
37#include <kernel/thread.h>
38#include <kernel/vm.h>
39
40#include <lib/bio.h>
41#include <lib/bootargs.h>
42#include <lib/bootimage.h>
43#include <lib/ptable.h>
44#include <lib/sysparam.h>
45
46#include <app/lkboot.h>
47
48#if PLATFORM_ZYNQ
49#include <platform/fpga.h>
50#include <platform/zynq.h>
51#endif
52
53#define bootdevice "spi0"
54
55#define LOCAL_TRACE 0
56
57struct lkb_command {
58 struct lkb_command *next;
59 const char *name;
60 lkb_handler_t handler;
61 void *cookie;
62};
63
64struct lkb_command *lkb_cmd_list = NULL;
65
66void lkb_register(const char *name, lkb_handler_t handler, void *cookie) {
67 struct lkb_command *cmd = malloc(sizeof(struct lkb_command));
68 if (cmd != NULL) {
69 cmd->next = lkb_cmd_list;
70 cmd->name = name;
71 cmd->handler = handler;
72 cmd->cookie = cookie;
73 lkb_cmd_list = cmd;
74 }
75}
76
77static int do_reboot(void *arg) {
78 thread_sleep(250);
79 platform_halt(HALT_ACTION_REBOOT, HALT_REASON_SW_RESET);
80 return 0;
81}
82
83struct chainload_args {
84 void *func;
85 ulong args[4];
86};
87
88static int chainload_thread(void *arg)
89{
90 struct chainload_args *args = (struct chainload_args *)arg;
91
92 thread_sleep(250);
93
94 TRACEF("chain loading address %p, args 0x%lx 0x%lx 0x%lx 0x%lx\n",
95 args->func, args->args[0], args->args[1], args->args[2], args->args[3]);
96 arch_chain_load((void *)args->func, args->args[0], args->args[1], args->args[2], args->args[3]);
97
98 for (;;);
99}
100
101static int do_boot(lkb_t *lkb, size_t len, const char **result)
102{
103 LTRACEF("lkb %p, len %zu, result %p\n", lkb, len, result);
104
105 void *buf;
106 paddr_t buf_phys;
107
108 if (vmm_alloc_contiguous(vmm_get_kernel_aspace(), "lkboot_iobuf",
109 len, &buf, log2_uint(1024*1024), 0, ARCH_MMU_FLAG_UNCACHED) < 0) {
110 *result = "not enough memory";
111 return -1;
112 }
113 arch_mmu_query((vaddr_t)buf, &buf_phys, NULL);
114 LTRACEF("iobuffer %p (phys 0x%lx)\n", buf, buf_phys);
115
116 if (lkb_read(lkb, buf, len)) {
117 *result = "io error";
118 // XXX free buffer here
119 return -1;
120 }
121
122 /* construct a boot argument list */
123 const size_t bootargs_size = PAGE_SIZE;
124#if 0
125 void *args = (void *)((uintptr_t)lkb_iobuffer + lkb_iobuffer_size - bootargs_size);
126 paddr_t args_phys = lkb_iobuffer_phys + lkb_iobuffer_size - bootargs_size;
127#elif PLATFORM_ZYNQ
128 /* grab the top page of sram */
129 /* XXX do this better */
130 paddr_t args_phys = SRAM_BASE + SRAM_SIZE - bootargs_size;
131 void *args = paddr_to_kvaddr(args_phys);
132#else
133#error need better way
134#endif
135 LTRACEF("boot args %p, phys 0x%lx, len %zu\n", args, args_phys, bootargs_size);
136
137 bootargs_start(args, bootargs_size);
138 bootargs_add_command_line(args, bootargs_size, "what what");
139 arch_clean_cache_range((vaddr_t)args, bootargs_size);
140
141 ulong lk_args[4];
142 bootargs_generate_lk_arg_values(args_phys, lk_args);
143
144 const void *ptr;
145
146 /* sniff it to see if it's a bootimage or a raw image */
147 bootimage_t *bi;
148 if (bootimage_open(buf, len, &bi) >= 0) {
149 size_t len;
150
151 /* it's a bootimage */
152 TRACEF("detected bootimage\n");
153
154 /* find the lk image */
155 if (bootimage_get_file_section(bi, TYPE_LK, &ptr, &len) >= 0) {
156 TRACEF("found lk section at %p\n", ptr);
157
158 /* add the boot image to the argument list */
159 size_t bootimage_size;
160 bootimage_get_range(bi, NULL, &bootimage_size);
161
162 bootargs_add_bootimage_pointer(args, bootargs_size, "pmem", buf_phys, bootimage_size);
163 }
164 } else {
165 /* raw image, just chain load it directly */
166 TRACEF("raw image, chainloading\n");
167
168 ptr = buf;
169 }
170
171 /* start a boot thread to complete the startup */
172 static struct chainload_args cl_args;
173
174 cl_args.func = (void *)ptr;
175 cl_args.args[0] = lk_args[0];
176 cl_args.args[1] = lk_args[1];
177 cl_args.args[2] = lk_args[2];
178 cl_args.args[3] = lk_args[3];
179
180 thread_resume(thread_create("boot", &chainload_thread, &cl_args,
181 DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
182
183 return 0;
184}
185
186/* try to boot the system from a flash partition */
187status_t do_flash_boot(void)
188{
189 status_t err;
190
191 LTRACE_ENTRY;
192
193 /* construct a boot argument list */
194 const size_t bootargs_size = PAGE_SIZE;
195#if 0
196 /* old code */
197 void *args = (void *)((uintptr_t)lkb_iobuffer + lkb_iobuffer_size - bootargs_size);
198 paddr_t args_phys = lkb_iobuffer_phys + lkb_iobuffer_size - bootargs_size;
199#elif PLATFORM_ZYNQ
200 /* grab the top page of sram */
201 paddr_t args_phys = SRAM_BASE + SRAM_SIZE - bootargs_size;
202 void *args = paddr_to_kvaddr(args_phys);
203#else
204#error need better way
205#endif
206 LTRACEF("boot args %p, phys 0x%lx, len %zu\n", args, args_phys, bootargs_size);
207
208 bootargs_start(args, bootargs_size);
209 bootargs_add_command_line(args, bootargs_size, "what what");
210 arch_clean_cache_range((vaddr_t)args, bootargs_size);
211
212 ulong lk_args[4];
213 bootargs_generate_lk_arg_values(args_phys, lk_args);
214
215 const void *ptr;
216
217 if (!ptable_found_valid()) {
218 TRACEF("ptable not found\n");
219 return ERR_NOT_FOUND;
220 }
221
222 /* find the system partition */
223 struct ptable_entry entry;
224 err = ptable_find("system", &entry);
225 if (err < 0) {
226 TRACEF("cannot find system partition\n");
227 return ERR_NOT_FOUND;
228 }
229
230 /* get a direct pointer to the device */
231 bdev_t *bdev = ptable_get_device();
232 if (!bdev) {
233 TRACEF("error opening boot device\n");
234 return ERR_NOT_FOUND;
235 }
236
237 /* convert the bdev to a memory pointer */
238 err = bio_ioctl(bdev, BIO_IOCTL_GET_MEM_MAP, (void *)&ptr);
239 TRACEF("err %d, ptr %p\n", err, ptr);
240 if (err < 0) {
241 TRACEF("error getting direct pointer to block device\n");
242 return ERR_NOT_FOUND;
243 }
244
245 /* sniff it to see if it's a bootimage or a raw image */
246 bootimage_t *bi;
247 if (bootimage_open((char *)ptr + entry.offset, entry.length, &bi) >= 0) {
248 size_t len;
249
250 /* it's a bootimage */
251 TRACEF("detected bootimage\n");
252
253 /* find the lk image */
254 if (bootimage_get_file_section(bi, TYPE_LK, &ptr, &len) >= 0) {
255 TRACEF("found lk section at %p\n", ptr);
256
257 /* add the boot image to the argument list */
258 size_t bootimage_size;
259 bootimage_get_range(bi, NULL, &bootimage_size);
260
261 bootargs_add_bootimage_pointer(args, bootargs_size, bdev->name, entry.offset, bootimage_size);
262 }
263 } else {
264 /* did not find a bootimage, abort */
265 bio_ioctl(bdev, BIO_IOCTL_PUT_MEM_MAP, NULL);
266 return ERR_NOT_FOUND;
267 }
268
269 TRACEF("chain loading binary at %p\n", ptr);
270 arch_chain_load((void *)ptr, lk_args[0], lk_args[1], lk_args[2], lk_args[3]);
271
272 /* put the block device back into block mode (though we never get here) */
273 bio_ioctl(bdev, BIO_IOCTL_PUT_MEM_MAP, NULL);
274
275 return NO_ERROR;
276}
277
278// return NULL for success, error string for failure
279int lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, size_t len, const char **result)
280{
281 *result = NULL;
282
283 struct lkb_command *lcmd;
284 for (lcmd = lkb_cmd_list; lcmd; lcmd = lcmd->next) {
285 if (!strcmp(lcmd->name, cmd)) {
286 *result = lcmd->handler(lkb, arg, len, lcmd->cookie);
287 return 0;
288 }
289 }
290
291 if (!strcmp(cmd, "flash") || !strcmp(cmd, "erase")) {
292 struct ptable_entry entry;
293 bdev_t *bdev;
294
295 if (ptable_find(arg, &entry) < 0) {
296 size_t plen = len;
297 /* doesn't exist, make one */
298 if (ptable_add(arg, plen, 0) < 0) {
299 *result = "error creating partition";
300 return -1;
301 }
302
303 if (ptable_find(arg, &entry) < 0) {
304 *result = "couldn't find partition after creating it";
305 return -1;
306 }
307 }
308 if (len > entry.length) {
309 *result = "partition too small";
310 return -1;
311 }
312
313 if (!(bdev = ptable_get_device())) {
314 *result = "ptable_get_device failed";
315 return -1;
316 }
317
318 printf("lkboot: erasing partition of size %llu\n", entry.length);
319 if (bio_erase(bdev, entry.offset, entry.length) != (ssize_t)entry.length) {
320 *result = "bio_erase failed";
321 return -1;
322 }
323
324 if (!strcmp(cmd, "flash")) {
325 printf("lkboot: writing to partition\n");
326
327 void *buf = malloc(bdev->block_size);
328 if (!buf) {
329 *result = "memory allocation failed";
330 return -1;
331 }
332
333 size_t pos = 0;
334 while (pos < len) {
335 size_t toread = MIN(len - pos, bdev->block_size);
336
337 LTRACEF("offset %zu, toread %zu\n", pos, toread);
338
339 if (lkb_read(lkb, buf, toread)) {
340 *result = "io error";
341 free(buf);
342 return -1;
343 }
344
345 if (bio_write(bdev, buf, entry.offset + pos, toread) != (ssize_t)toread) {
346 *result = "bio_write failed";
347 free(buf);
348 return -1;
349 }
350
351 pos += toread;
352 }
353
354 free(buf);
355 }
356 } else if (!strcmp(cmd, "remove")) {
357 if (ptable_remove(arg) < 0) {
358 *result = "remove failed";
359 return -1;
360 }
361 } else if (!strcmp(cmd, "fpga")) {
362#if PLATFORM_ZYNQ
363 void *buf = malloc(len);
364 if (!buf) {
365 *result = "error allocating buffer";
366 return -1;
367 }
368
369 /* translate to physical address */
370 paddr_t pa = kvaddr_to_paddr(buf);
371 if (pa == 0) {
372 *result = "error allocating buffer";
373 free(buf);
374 return -1;
375
376 }
377
378 if (lkb_read(lkb, buf, len)) {
379 *result = "io error";
380 free(buf);
381 return -1;
382 }
383
384 /* make sure the cache is flushed for this buffer for DMA coherency purposes */
385 arch_clean_cache_range((vaddr_t)buf, len);
386
387 /* program the fpga */
388 zynq_reset_fpga();
389 zynq_program_fpga(pa, len);
390
391 free(buf);
392#else
393 *result = "no fpga";
394 return -1;
395#endif
396 } else if (!strcmp(cmd, "boot")) {
397 return do_boot(lkb, len, result);
398 } else if (!strcmp(cmd, "getsysparam")) {
399 const void *ptr;
400 size_t len;
401 if (sysparam_get_ptr(arg, &ptr, &len) == 0) {
402 lkb_write(lkb, ptr, len);
403 }
404 } else if (!strcmp(cmd, "reboot")) {
405 thread_resume(thread_create("reboot", &do_reboot, NULL,
406 DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
407 } else {
408 *result = "unknown command";
409 return -1;
410 }
411
412 return 0;
413}