2 * Copyright (C) 2010-2014 Nathan Whitehorn
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
30 #include <sys/endian.h>
31 #include <sys/param.h>
32 #include <fdt_platform.h>
34 #include <machine/cpufunc.h>
35 #include <bootstrap.h>
36 #include "host_syscall.h"
39 struct arch_switch archsw;
42 int kboot_getdev(void **vdev, const char *devspec, const char **path);
43 ssize_t kboot_copyin(const void *src, vm_offset_t dest, const size_t len);
44 ssize_t kboot_copyout(vm_offset_t src, void *dest, const size_t len);
45 ssize_t kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len);
46 int kboot_autoload(void);
47 uint64_t kboot_loadaddr(u_int type, void *data, uint64_t addr);
48 int kboot_setcurrdev(struct env_var *ev, int flags, const void *value);
49 static void kboot_kseg_get(int *nseg, void **ptr);
51 extern int command_fdt_internal(int argc, char *argv[]);
59 kboot_get_phys_load_segment(void)
63 static uint64_t load_segment = ~(0UL);
66 struct region_desc rsvd_reg[32];
71 if (load_segment == ~(0UL)) {
73 /* Default load address is 0x00000000 */
76 /* Read reserved regions */
77 fd = host_open("/proc/device-tree/reserved-ranges", O_RDONLY, 0);
79 while (host_read(fd, &entry[0], sizeof(entry)) == sizeof(entry)) {
80 rsvd_reg[rsvd_reg_cnt].start = be64toh(entry[0]);
81 rsvd_reg[rsvd_reg_cnt].end =
82 be64toh(entry[1]) + rsvd_reg[rsvd_reg_cnt].start - 1;
87 /* Read where the kernel ends */
88 fd = host_open("/proc/device-tree/chosen/linux,kernel-end", O_RDONLY, 0);
90 ret = host_read(fd, &val_64, sizeof(val_64));
92 if (ret == sizeof(uint64_t)) {
93 rsvd_reg[rsvd_reg_cnt].start = 0;
94 rsvd_reg[rsvd_reg_cnt].end = be64toh(val_64) - 1;
96 memcpy(&val_32, &val_64, sizeof(val_32));
97 rsvd_reg[rsvd_reg_cnt].start = 0;
98 rsvd_reg[rsvd_reg_cnt].end = be32toh(val_32) - 1;
104 /* Read memory size (SOCKET0 only) */
105 fd = host_open("/proc/device-tree/memory@0/reg", O_RDONLY, 0);
107 fd = host_open("/proc/device-tree/memory/reg", O_RDONLY, 0);
109 ret = host_read(fd, &entry, sizeof(entry));
111 /* Memory range in start:length format */
112 entry[0] = be64toh(entry[0]);
113 entry[1] = be64toh(entry[1]);
115 /* Reserve everything what is before start */
117 rsvd_reg[rsvd_reg_cnt].start = 0;
118 rsvd_reg[rsvd_reg_cnt].end = entry[0] - 1;
121 /* Reserve everything what is after end */
122 if (entry[1] != 0xffffffffffffffffUL) {
123 rsvd_reg[rsvd_reg_cnt].start = entry[0] + entry[1];
124 rsvd_reg[rsvd_reg_cnt].end = 0xffffffffffffffffUL;
131 /* Sort entries in ascending order (bubble) */
132 for (a = rsvd_reg_cnt - 1; a > 0; a--) {
133 for (b = 0; b < a; b++) {
134 if (rsvd_reg[b].start > rsvd_reg[b + 1].start) {
135 struct region_desc tmp;
137 rsvd_reg[b] = rsvd_reg[b + 1];
138 rsvd_reg[b + 1] = tmp;
143 /* Join overlapping/adjacent regions */
144 for (a = 0; a < rsvd_reg_cnt - 1; ) {
146 if ((rsvd_reg[a + 1].start >= rsvd_reg[a].start) &&
147 ((rsvd_reg[a + 1].start - 1) <= rsvd_reg[a].end)) {
148 /* We have overlapping/adjacent regions! */
150 MAX(rsvd_reg[a].end, rsvd_reg[a + a].end);
152 for (b = a + 1; b < rsvd_reg_cnt - 1; b++)
153 rsvd_reg[b] = rsvd_reg[b + 1];
159 /* Find the first free region */
160 if (rsvd_reg_cnt > 0) {
162 end = rsvd_reg[0].start;
163 for (a = 0; a < rsvd_reg_cnt - 1; a++) {
164 if ((start >= rsvd_reg[a].start) &&
165 (start <= rsvd_reg[a].end)) {
166 start = rsvd_reg[a].end + 1;
167 end = rsvd_reg[a + 1].start;
173 uint64_t align = 64UL*1024UL*1024UL;
175 /* Align both to 64MB boundary */
176 start = (start + align - 1UL) & ~(align - 1UL);
177 end = ((end + 1UL) & ~(align - 1UL)) - 1UL;
180 load_segment = start;
185 return (load_segment);
189 kboot_get_kernel_machine_bits(void)
191 static uint8_t bits = 0;
192 struct old_utsname utsname;
196 /* Default is 32-bit kernel */
199 /* Try to get system type */
200 memset(&utsname, 0, sizeof(utsname));
201 ret = host_uname(&utsname);
203 if (strcmp(utsname.machine, "ppc64") == 0)
205 else if (strcmp(utsname.machine, "ppc64le") == 0)
214 kboot_getdev(void **vdev, const char *devspec, const char **path)
217 const char *devpath, *filepath;
219 struct devdesc *desc;
221 if (strchr(devspec, ':') != NULL) {
223 filepath = strchr(devspec, ':') + 1;
225 devpath = getenv("currdev");
229 for (i = 0; (dv = devsw[i]) != NULL; i++) {
230 if (strncmp(dv->dv_name, devpath, strlen(dv->dv_name)) == 0)
236 if (path != NULL && filepath != NULL)
238 else if (path != NULL)
239 *path = strchr(devspec, ':') + 1;
242 desc = malloc(sizeof(*desc));
245 desc->d_opendata = strdup(devpath);
253 main(int argc, const char **argv)
256 const size_t heapsize = 15*1024*1024;
260 * Set the heap to one page after the end of the loader.
262 heapbase = host_getmem(heapsize);
263 setheap(heapbase, heapbase + heapsize);
270 /* Choose bootdev if provided */
276 printf("Boot device: %s\n", bootdev);
278 archsw.arch_getdev = kboot_getdev;
279 archsw.arch_copyin = kboot_copyin;
280 archsw.arch_copyout = kboot_copyout;
281 archsw.arch_readin = kboot_readin;
282 archsw.arch_autoload = kboot_autoload;
283 archsw.arch_loadaddr = kboot_loadaddr;
284 archsw.arch_kexec_kseg_get = kboot_kseg_get;
286 printf("\n%s", bootprog_info);
288 setenv("currdev", bootdev, 1);
289 setenv("loaddev", bootdev, 1);
290 setenv("LINES", "24", 1);
291 setenv("usefdt", "1", 1);
293 interact(); /* doesn't return */
301 while (1); /* XXX: host_exit */
308 struct host_timeval tvi, tv;
310 host_gettimeofday(&tvi, NULL);
311 ti = tvi.tv_sec*1000000 + tvi.tv_usec;
313 host_gettimeofday(&tv, NULL);
314 t = tv.tv_sec*1000000 + tv.tv_usec;
315 } while (t < ti + usecs);
321 struct host_timeval tv;
322 host_gettimeofday(&tv, NULL);
338 struct kexec_segment {
345 struct kexec_segment loaded_segments[128];
346 int nkexec_segments = 0;
349 get_phys_buffer(vm_offset_t dest, const size_t len, void **buf)
352 const size_t segsize = 4*1024*1024;
354 for (i = 0; i < nkexec_segments; i++) {
355 if (dest >= (vm_offset_t)loaded_segments[i].mem &&
356 dest < (vm_offset_t)loaded_segments[i].mem +
357 loaded_segments[i].memsz)
361 loaded_segments[nkexec_segments].buf = host_getmem(segsize);
362 loaded_segments[nkexec_segments].bufsz = segsize;
363 loaded_segments[nkexec_segments].mem = (void *)rounddown2(dest,segsize);
364 loaded_segments[nkexec_segments].memsz = segsize;
370 *buf = loaded_segments[i].buf + (dest -
371 (vm_offset_t)loaded_segments[i].mem);
372 return (min(len,loaded_segments[i].bufsz - (dest -
373 (vm_offset_t)loaded_segments[i].mem)));
377 kboot_copyin(const void *src, vm_offset_t dest, const size_t len)
379 ssize_t segsize, remainder;
384 segsize = get_phys_buffer(dest, remainder, &destbuf);
385 bcopy(src, destbuf, segsize);
386 remainder -= segsize;
389 } while (remainder > 0);
395 kboot_copyout(vm_offset_t src, void *dest, const size_t len)
397 ssize_t segsize, remainder;
402 segsize = get_phys_buffer(src, remainder, &srcbuf);
403 bcopy(srcbuf, dest, segsize);
404 remainder -= segsize;
407 } while (remainder > 0);
413 kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len)
416 size_t resid, chunk, get;
422 chunk = min(PAGE_SIZE, len);
425 printf("kboot_readin: buf malloc failed\n");
429 for (resid = len; resid > 0; resid -= got, p += got) {
430 get = min(chunk, resid);
431 got = VECTX_READ(fd, buf, get);
434 printf("kboot_readin: read failed\n");
438 kboot_copyin(buf, p, got);
442 return (len - resid);
453 kboot_loadaddr(u_int type, void *data, uint64_t addr)
456 if (type == LOAD_ELF)
457 addr = roundup(addr, PAGE_SIZE);
459 addr += kboot_get_phys_load_segment();
465 kboot_kseg_get(int *nseg, void **ptr)
470 for (a = 0; a < nkexec_segments; a++) {
471 printf("kseg_get: %jx %jx %jx %jx\n",
472 (uintmax_t)loaded_segments[a].buf,
473 (uintmax_t)loaded_segments[a].bufsz,
474 (uintmax_t)loaded_segments[a].mem,
475 (uintmax_t)loaded_segments[a].memsz);
479 *nseg = nkexec_segments;
480 *ptr = &loaded_segments[0];
484 _start(int argc, const char **argv, char **env)
486 // This makes error "variable 'sp' is uninitialized" be just a warning on clang.
487 // Initializing 'sp' is not desired here as it would overwrite "r1" original value
488 #if defined(__clang__)
489 #pragma clang diagnostic push
490 #pragma clang diagnostic warning "-Wuninitialized"
492 register volatile void **sp asm("r1");
493 main((int)sp[0], (const char **)&sp[1]);
494 #if defined(__clang__)
495 #pragma clang diagnostic pop
501 * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
502 * and declaring it as extern is in contradiction with COMMAND_SET() macro
503 * (which uses static pointer), we're defining wrapper function, which
504 * calls the proper fdt handling routine.
507 command_fdt(int argc, char *argv[])
510 return (command_fdt_internal(argc, argv));
513 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);