1 /*-
2 * Copyright (C) 2010-2014 Nathan Whitehorn
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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.
24 */
25
26 #include <sys/cdefs.h>
27 #include <stand.h>
28 #include <sys/param.h>
29 #include <sys/boot.h>
30 #include <fdt_platform.h>
31
32 #include <machine/cpufunc.h>
33 #include <bootstrap.h>
34 #include "host_syscall.h"
35 #include "kboot.h"
36
37 struct arch_switch archsw;
38 extern void *_end;
39
40 int kboot_getdev(void **vdev, const char *devspec, const char **path);
41 ssize_t kboot_copyin(const void *src, vm_offset_t dest, const size_t len);
42 ssize_t kboot_copyout(vm_offset_t src, void *dest, const size_t len);
43 ssize_t kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len);
44 int kboot_autoload(void);
45 uint64_t kboot_loadaddr(u_int type, void *data, uint64_t addr);
46 static void kboot_kseg_get(int *nseg, void **ptr);
47 static void kboot_zfs_probe(void);
48
49 extern int command_fdt_internal(int argc, char *argv[]);
50
51 /*
52 * NB: getdev should likely be identical to this most places, except maybe
53 * we should move to storing the length of the platform devdesc.
54 */
55 int
kboot_getdev(void ** vdev,const char * devspec,const char ** path)56 kboot_getdev(void **vdev, const char *devspec, const char **path)
57 {
58 int rv;
59 struct devdesc **dev = (struct devdesc **)vdev;
60
61 /*
62 * If it looks like this is just a path and no device, go with the
63 * current device.
64 */
65 if (devspec == NULL || strchr(devspec, ':') == NULL) {
66 if (((rv = devparse(dev, getenv("currdev"), NULL)) == 0) &&
67 (path != NULL))
68 *path = devspec;
69 return (rv);
70 }
71
72 /*
73 * Try to parse the device name off the beginning of the devspec
74 */
75 return (devparse(dev, devspec, path));
76 }
77
78 static int
parse_args(int argc,const char ** argv)79 parse_args(int argc, const char **argv)
80 {
81 int howto = 0;
82
83 /*
84 * When run as init, sometimes argv[0] is a EFI-ESP path, other times
85 * it's the name of the init program, and sometimes it's a placeholder
86 * string, so we exclude it here. For the other args, look for DOS-like
87 * and Unix-like absolte paths and exclude parsing it if we find that,
88 * otherwise parse it as a command arg (so looking for '-X', 'foo' or
89 * 'foo=bar'). This is a little different than EFI where it argv[0]
90 * often times is the first argument passed in. There are cases when
91 * linux-booting via EFI that we have the EFI path we used to run
92 * bootXXX.efi as the arguments to init, so we need to exclude the paths
93 * there as well.
94 */
95 for (int i = 1; i < argc; i++) {
96 if (argv[i][0] != '\\' && argv[i][0] != '/') {
97 howto |= boot_parse_arg(argv[i]);
98 }
99 }
100
101 return (howto);
102 }
103
104 static vm_offset_t rsdp;
105
106 static vm_offset_t
kboot_rsdp_from_efi(void)107 kboot_rsdp_from_efi(void)
108 {
109 char buffer[512 + 1];
110 char *walker, *ep;
111
112 if (!file2str("/sys/firmware/efi/systab", buffer, sizeof(buffer)))
113 return (0); /* Not an EFI system */
114 ep = buffer + strlen(buffer);
115 walker = buffer;
116 while (walker < ep) {
117 if (strncmp("ACPI20=", walker, 7) == 0)
118 return((vm_offset_t)strtoull(walker + 7, NULL, 0));
119 if (strncmp("ACPI=", walker, 5) == 0)
120 return((vm_offset_t)strtoull(walker + 5, NULL, 0));
121 walker += strcspn(walker, "\n");
122 }
123 return (0);
124 }
125
126 static void
find_acpi(void)127 find_acpi(void)
128 {
129 rsdp = kboot_rsdp_from_efi();
130 #if 0 /* maybe for amd64 */
131 if (rsdp == 0)
132 rsdp = find_rsdp_arch();
133 #endif
134 }
135
136 vm_offset_t
acpi_rsdp(void)137 acpi_rsdp(void)
138 {
139 return (rsdp);
140 }
141
142 bool
has_acpi(void)143 has_acpi(void)
144 {
145 return rsdp != 0;
146 }
147
148 int
main(int argc,const char ** argv)149 main(int argc, const char **argv)
150 {
151 void *heapbase;
152 const size_t heapsize = 128*1024*1024;
153 const char *bootdev;
154
155 archsw.arch_getdev = kboot_getdev;
156 archsw.arch_copyin = kboot_copyin;
157 archsw.arch_copyout = kboot_copyout;
158 archsw.arch_readin = kboot_readin;
159 archsw.arch_autoload = kboot_autoload;
160 archsw.arch_loadaddr = kboot_loadaddr;
161 archsw.arch_kexec_kseg_get = kboot_kseg_get;
162 archsw.arch_zfs_probe = kboot_zfs_probe;
163
164 /* Give us a sane world if we're running as init */
165 do_init();
166
167 /*
168 * Setup the heap 15MB should be plenty
169 */
170 heapbase = host_getmem(heapsize);
171 setheap(heapbase, heapbase + heapsize);
172
173 /* Parse the command line args -- ignoring for now the console selection */
174 parse_args(argc, argv);
175
176 /*
177 * Set up console.
178 */
179 cons_probe();
180
181 /* Initialize all the devices */
182 devinit();
183
184 bootdev = getenv("bootdev");
185 if (bootdev == NULL)
186 bootdev="zfs:";
187 hostfs_root = getenv("hostfs_root");
188 if (hostfs_root == NULL)
189 hostfs_root = "/";
190 #if defined(LOADER_ZFS_SUPPORT)
191 if (strcmp(bootdev, "zfs:") == 0) {
192 /*
193 * Pseudo device that says go find the right ZFS pool. This will be
194 * the first pool that we find that passes the sanity checks (eg looks
195 * like it might be vbootable) and sets currdev to the right thing based
196 * on active BEs, etc
197 */
198 hostdisk_zfs_find_default();
199 } else
200 #endif
201 {
202 /*
203 * Otherwise, honor what's on the command line. If we've been
204 * given a specific ZFS partition, then we'll honor it w/o BE
205 * processing that would otherwise pick a different snapshot to
206 * boot than the default one in the pool.
207 */
208 set_currdev(bootdev);
209 }
210
211 printf("Boot device: %s with hostfs_root %s\n", bootdev, hostfs_root);
212
213 printf("\n%s", bootprog_info);
214
215 setenv("LINES", "24", 1);
216 setenv("usefdt", "1", 1);
217
218 /*
219 * Find acpi, if it exists
220 */
221 find_acpi();
222
223 interact(); /* doesn't return */
224
225 return (0);
226 }
227
228 void
exit(int code)229 exit(int code)
230 {
231 host_exit(code);
232 __unreachable();
233 }
234
235 void
delay(int usecs)236 delay(int usecs)
237 {
238 struct host_timeval tvi, tv;
239 uint64_t ti, t;
240 host_gettimeofday(&tvi, NULL);
241 ti = tvi.tv_sec*1000000 + tvi.tv_usec;
242 do {
243 host_gettimeofday(&tv, NULL);
244 t = tv.tv_sec*1000000 + tv.tv_usec;
245 } while (t < ti + usecs);
246 }
247
248 time_t
getsecs(void)249 getsecs(void)
250 {
251 struct host_timeval tv;
252 host_gettimeofday(&tv, NULL);
253 return (tv.tv_sec);
254 }
255
256 time_t
time(time_t * tloc)257 time(time_t *tloc)
258 {
259 time_t rv;
260
261 rv = getsecs();
262 if (tloc != NULL)
263 *tloc = rv;
264
265 return (rv);
266 }
267
268 struct host_kexec_segment loaded_segments[HOST_KEXEC_SEGMENT_MAX];
269 int nkexec_segments = 0;
270
271 static ssize_t
get_phys_buffer(vm_offset_t dest,const size_t len,void ** buf)272 get_phys_buffer(vm_offset_t dest, const size_t len, void **buf)
273 {
274 int i = 0;
275 const size_t segsize = 8*1024*1024;
276
277 if (nkexec_segments == HOST_KEXEC_SEGMENT_MAX)
278 panic("Tried to load too many kexec segments");
279 for (i = 0; i < nkexec_segments; i++) {
280 if (dest >= (vm_offset_t)loaded_segments[i].mem &&
281 dest < (vm_offset_t)loaded_segments[i].mem +
282 loaded_segments[i].memsz)
283 goto out;
284 }
285
286 loaded_segments[nkexec_segments].buf = host_getmem(segsize);
287 loaded_segments[nkexec_segments].bufsz = segsize;
288 loaded_segments[nkexec_segments].mem = (void *)rounddown2(dest,segsize);
289 loaded_segments[nkexec_segments].memsz = segsize;
290
291 i = nkexec_segments;
292 nkexec_segments++;
293
294 out:
295 *buf = loaded_segments[i].buf + (dest -
296 (vm_offset_t)loaded_segments[i].mem);
297 return (min(len,loaded_segments[i].bufsz - (dest -
298 (vm_offset_t)loaded_segments[i].mem)));
299 }
300
301 ssize_t
kboot_copyin(const void * src,vm_offset_t dest,const size_t len)302 kboot_copyin(const void *src, vm_offset_t dest, const size_t len)
303 {
304 ssize_t segsize, remainder;
305 void *destbuf;
306
307 remainder = len;
308 do {
309 segsize = get_phys_buffer(dest, remainder, &destbuf);
310 bcopy(src, destbuf, segsize);
311 remainder -= segsize;
312 src += segsize;
313 dest += segsize;
314 } while (remainder > 0);
315
316 return (len);
317 }
318
319 ssize_t
kboot_copyout(vm_offset_t src,void * dest,const size_t len)320 kboot_copyout(vm_offset_t src, void *dest, const size_t len)
321 {
322 ssize_t segsize, remainder;
323 void *srcbuf;
324
325 remainder = len;
326 do {
327 segsize = get_phys_buffer(src, remainder, &srcbuf);
328 bcopy(srcbuf, dest, segsize);
329 remainder -= segsize;
330 src += segsize;
331 dest += segsize;
332 } while (remainder > 0);
333
334 return (len);
335 }
336
337 ssize_t
kboot_readin(readin_handle_t fd,vm_offset_t dest,const size_t len)338 kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len)
339 {
340 void *buf;
341 size_t resid, chunk, get;
342 ssize_t got;
343 vm_offset_t p;
344
345 p = dest;
346
347 chunk = min(PAGE_SIZE, len);
348 buf = malloc(chunk);
349 if (buf == NULL) {
350 printf("kboot_readin: buf malloc failed\n");
351 return (0);
352 }
353
354 for (resid = len; resid > 0; resid -= got, p += got) {
355 get = min(chunk, resid);
356 got = VECTX_READ(fd, buf, get);
357 if (got <= 0) {
358 if (got < 0)
359 printf("kboot_readin: read failed\n");
360 break;
361 }
362
363 kboot_copyin(buf, p, got);
364 }
365
366 free (buf);
367 return (len - resid);
368 }
369
370 int
kboot_autoload(void)371 kboot_autoload(void)
372 {
373
374 return (0);
375 }
376
377 uint64_t
kboot_loadaddr(u_int type,void * data,uint64_t addr)378 kboot_loadaddr(u_int type, void *data, uint64_t addr)
379 {
380
381 if (type == LOAD_ELF)
382 addr = roundup(addr, PAGE_SIZE);
383 else
384 addr += kboot_get_phys_load_segment();
385
386 return (addr);
387 }
388
389 static void
kboot_kseg_get(int * nseg,void ** ptr)390 kboot_kseg_get(int *nseg, void **ptr)
391 {
392 #if 0
393 int a;
394
395 for (a = 0; a < nkexec_segments; a++) {
396 printf("kseg_get: %jx %jx %jx %jx\n",
397 (uintmax_t)loaded_segments[a].buf,
398 (uintmax_t)loaded_segments[a].bufsz,
399 (uintmax_t)loaded_segments[a].mem,
400 (uintmax_t)loaded_segments[a].memsz);
401 }
402 #endif
403
404 *nseg = nkexec_segments;
405 *ptr = &loaded_segments[0];
406 }
407
408 static void
kboot_zfs_probe(void)409 kboot_zfs_probe(void)
410 {
411 #if defined(LOADER_ZFS_SUPPORT)
412 /*
413 * Open all the disks and partitions we can find to see if there are ZFS
414 * pools on them.
415 */
416 hostdisk_zfs_probe();
417 #endif
418 }
419
420 /*
421 * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
422 * and declaring it as extern is in contradiction with COMMAND_SET() macro
423 * (which uses static pointer), we're defining wrapper function, which
424 * calls the proper fdt handling routine.
425 */
426 static int
command_fdt(int argc,char * argv[])427 command_fdt(int argc, char *argv[])
428 {
429
430 return (command_fdt_internal(argc, argv));
431 }
432
433 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
434