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/param.h>
32 #include <fdt_platform.h>
34 #include <machine/cpufunc.h>
35 #include <bootstrap.h>
36 #include "host_syscall.h"
40 struct arch_switch archsw;
43 int kboot_getdev(void **vdev, const char *devspec, const char **path);
44 ssize_t kboot_copyin(const void *src, vm_offset_t dest, const size_t len);
45 ssize_t kboot_copyout(vm_offset_t src, void *dest, const size_t len);
46 ssize_t kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len);
47 int kboot_autoload(void);
48 static void kboot_zfs_probe(void);
50 extern int command_fdt_internal(int argc, char *argv[]);
52 #define PA_INVAL (vm_offset_t)-1
53 static vm_offset_t pa_start = PA_INVAL;
54 static vm_offset_t padding;
55 static vm_offset_t offset;
57 static uint64_t commit_limit;
58 static uint64_t committed_as;
59 static uint64_t mem_avail;
68 * To properly size the slabs, we need to find how much memory we can
69 * commit to using. commit_limit is the max, while commited_as is the
70 * current total. We can use these later to allocate the largetst amount
71 * of memory possible so we can support larger ram disks than we could
72 * by using fixed segment sizes. We also grab the memory available so
73 * we don't use more than 49% of that.
75 fd = open("host:/proc/meminfo", O_RDONLY);
77 while (fgetstr(buf, sizeof(buf), fd) > 0) {
78 if (strncmp(buf, "MemAvailable:", 13) == 0) {
79 mem_avail = strtoll(buf + 13, NULL, 0);
80 mem_avail <<= 10; /* Units are kB */
81 } else if (strncmp(buf, "CommitLimit:", 12) == 0) {
82 commit_limit = strtoll(buf + 13, NULL, 0);
83 commit_limit <<= 10; /* Units are kB */
84 } else if (strncmp(buf, "Committed_AS:", 13) == 0) {
85 committed_as = strtoll(buf + 14, NULL, 0);
86 committed_as <<= 10; /* Units are kB */
90 printf("Commit limit: %lld Committed bytes %lld Available %lld\n",
91 (long long)commit_limit, (long long)committed_as,
92 (long long)mem_avail);
97 * NB: getdev should likely be identical to this most places, except maybe
98 * we should move to storing the length of the platform devdesc.
101 kboot_getdev(void **vdev, const char *devspec, const char **path)
103 struct devdesc **dev = (struct devdesc **)vdev;
107 * If it looks like this is just a path and no device, go with the
110 if (devspec == NULL || strchr(devspec, ':') == NULL) {
111 if (((rv = devparse(dev, getenv("currdev"), NULL)) == 0) &&
118 * Try to parse the device name off the beginning of the devspec
120 return (devparse(dev, devspec, path));
124 parse_args(int argc, const char **argv)
129 * When run as init, sometimes argv[0] is a EFI-ESP path, other times
130 * it's the name of the init program, and sometimes it's a placeholder
131 * string, so we exclude it here. For the other args, look for DOS-like
132 * and Unix-like absolte paths and exclude parsing it if we find that,
133 * otherwise parse it as a command arg (so looking for '-X', 'foo' or
134 * 'foo=bar'). This is a little different than EFI where it argv[0]
135 * often times is the first argument passed in. There are cases when
136 * linux-booting via EFI that we have the EFI path we used to run
137 * bootXXX.efi as the arguments to init, so we need to exclude the paths
140 for (int i = 1; i < argc; i++) {
141 if (argv[i][0] != '\\' && argv[i][0] != '/') {
142 howto |= boot_parse_arg(argv[i]);
149 static vm_offset_t rsdp;
152 kboot_rsdp_from_efi(void)
154 char buffer[512 + 1];
157 if (!file2str("/sys/firmware/efi/systab", buffer, sizeof(buffer)))
158 return (0); /* Not an EFI system */
159 ep = buffer + strlen(buffer);
161 while (walker < ep) {
162 if (strncmp("ACPI20=", walker, 7) == 0)
163 return((vm_offset_t)strtoull(walker + 7, NULL, 0));
164 if (strncmp("ACPI=", walker, 5) == 0)
165 return((vm_offset_t)strtoull(walker + 5, NULL, 0));
166 walker += strcspn(walker, "\n");
174 rsdp = kboot_rsdp_from_efi();
175 #if 0 /* maybe for amd64 */
177 rsdp = find_rsdp_arch();
194 main(int argc, const char **argv)
197 const size_t heapsize = 64*1024*1024;
200 archsw.arch_getdev = kboot_getdev;
201 archsw.arch_copyin = kboot_copyin;
202 archsw.arch_copyout = kboot_copyout;
203 archsw.arch_readin = kboot_readin;
204 archsw.arch_autoload = kboot_autoload;
205 archsw.arch_zfs_probe = kboot_zfs_probe;
207 /* Give us a sane world if we're running as init */
211 * Setup the heap, 64MB is minimum for ZFS booting
213 heapbase = host_getmem(heapsize);
214 setheap(heapbase, heapbase + heapsize);
216 /* Parse the command line args -- ignoring for now the console selection */
217 parse_args(argc, argv);
224 /* Initialize all the devices */
227 bootdev = getenv("bootdev");
229 bootdev = hostdisk_gen_probe();
232 hostfs_root = getenv("hostfs_root");
233 if (hostfs_root == NULL)
235 #if defined(LOADER_ZFS_SUPPORT)
236 if (strcmp(bootdev, "zfs:") == 0) {
238 * Pseudo device that says go find the right ZFS pool. This will be
239 * the first pool that we find that passes the sanity checks (eg looks
240 * like it might be vbootable) and sets currdev to the right thing based
243 hostdisk_zfs_find_default();
248 * Otherwise, honor what's on the command line. If we've been
249 * given a specific ZFS partition, then we'll honor it w/o BE
250 * processing that would otherwise pick a different snapshot to
251 * boot than the default one in the pool.
253 set_currdev(bootdev);
256 printf("Boot device: %s with hostfs_root %s\n", bootdev, hostfs_root);
258 printf("\n%s", bootprog_info);
260 setenv("LINES", "24", 1);
263 enumerate_memory_arch();
266 * Find acpi, if it exists
270 interact(); /* doesn't return */
285 struct host_timeval tvi, tv;
287 host_gettimeofday(&tvi, NULL);
288 ti = tvi.tv_sec*1000000 + tvi.tv_usec;
290 host_gettimeofday(&tv, NULL);
291 t = tv.tv_sec*1000000 + tv.tv_usec;
292 } while (t < ti + usecs);
298 struct host_timeval tv;
299 host_gettimeofday(&tv, NULL);
315 struct host_kexec_segment loaded_segments[HOST_KEXEC_SEGMENT_MAX];
316 int nkexec_segments = 0;
318 #define SEGALIGN (1ul<<20)
321 get_phys_buffer(vm_offset_t dest, const size_t len, void **buf)
324 const size_t segsize = 64*1024*1024;
326 if (nkexec_segments == HOST_KEXEC_SEGMENT_MAX)
327 panic("Tried to load too many kexec segments");
328 for (i = 0; i < nkexec_segments; i++) {
329 if (dest >= (vm_offset_t)loaded_segments[i].mem &&
330 dest < (vm_offset_t)loaded_segments[i].mem +
331 loaded_segments[i].memsz)
335 loaded_segments[nkexec_segments].buf = host_getmem(segsize);
336 loaded_segments[nkexec_segments].bufsz = segsize;
337 loaded_segments[nkexec_segments].mem = (void *)rounddown2(dest,SEGALIGN);
338 loaded_segments[nkexec_segments].memsz = segsize;
344 *buf = loaded_segments[i].buf + (dest -
345 (vm_offset_t)loaded_segments[i].mem);
346 return (min(len,loaded_segments[i].bufsz - (dest -
347 (vm_offset_t)loaded_segments[i].mem)));
351 kboot_copyin(const void *src, vm_offset_t dest, const size_t len)
353 ssize_t segsize, remainder;
356 if (pa_start == PA_INVAL) {
357 pa_start = kboot_get_phys_load_segment();
358 // padding = 2 << 20; /* XXX amd64: revisit this when we make it work */
361 get_phys_buffer(pa_start, len, &destbuf);
366 segsize = get_phys_buffer(dest + pa_start + padding - offset, remainder, &destbuf);
367 bcopy(src, destbuf, segsize);
368 remainder -= segsize;
371 } while (remainder > 0);
377 kboot_copyout(vm_offset_t src, void *dest, const size_t len)
379 ssize_t segsize, remainder;
384 segsize = get_phys_buffer(src + pa_start + padding - offset, remainder, &srcbuf);
385 bcopy(srcbuf, dest, segsize);
386 remainder -= segsize;
389 } while (remainder > 0);
395 kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len)
398 size_t resid, chunk, get;
404 chunk = min(PAGE_SIZE, len);
407 printf("kboot_readin: buf malloc failed\n");
411 for (resid = len; resid > 0; resid -= got, p += got) {
412 get = min(chunk, resid);
413 got = VECTX_READ(fd, buf, get);
416 printf("kboot_readin: read failed\n");
420 kboot_copyin(buf, p, got);
424 return (len - resid);
435 kboot_kseg_get(int *nseg, void **ptr)
439 printf("kseg_get: %d segments\n", nkexec_segments);
440 printf("VA SZ PA MEMSZ\n");
441 printf("---------------- -------- ---------------- -----\n");
442 for (a = 0; a < nkexec_segments; a++) {
443 printf("%016jx %08jx %016jx %08jx\n",
444 (uintmax_t)loaded_segments[a].buf,
445 (uintmax_t)loaded_segments[a].bufsz,
446 (uintmax_t)loaded_segments[a].mem,
447 (uintmax_t)loaded_segments[a].memsz);
450 *nseg = nkexec_segments;
451 *ptr = &loaded_segments[0];
455 kboot_zfs_probe(void)
457 #if defined(LOADER_ZFS_SUPPORT)
459 * Open all the disks and partitions we can find to see if there are ZFS
462 hostdisk_zfs_probe();
467 * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
468 * and declaring it as extern is in contradiction with COMMAND_SET() macro
469 * (which uses static pointer), we're defining wrapper function, which
470 * calls the proper fdt handling routine.
473 command_fdt(int argc, char *argv[])
476 return (command_fdt_internal(argc, argv));
479 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);