]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/kboot/main.c
kboot: Don't need an arch pointer to get segments
[FreeBSD/FreeBSD.git] / stand / kboot / main.c
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 __FBSDID("$FreeBSD$");
28
29 #include <stand.h>
30 #include <sys/param.h>
31 #include <sys/boot.h>
32 #include <fdt_platform.h>
33
34 #include <machine/cpufunc.h>
35 #include <bootstrap.h>
36 #include "host_syscall.h"
37 #include "kboot.h"
38 #include "stand.h"
39
40 struct arch_switch      archsw;
41 extern void *_end;
42
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);
49
50 extern int command_fdt_internal(int argc, char *argv[]);
51
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;
56
57 static uint64_t commit_limit;
58 static uint64_t committed_as;
59 static uint64_t mem_avail;
60
61 static void
62 memory_limits(void)
63 {
64         int fd;
65         char buf[128];
66
67         /*
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.
74          */
75         fd = open("host:/proc/meminfo", O_RDONLY);
76         if (fd != -1) {
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 */
87                         }
88                 }
89         }
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);
93         close(fd);
94 }
95
96 /*
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.
99  */
100 int
101 kboot_getdev(void **vdev, const char *devspec, const char **path)
102 {
103         struct devdesc **dev = (struct devdesc **)vdev;
104         int                             rv;
105
106         /*
107          * If it looks like this is just a path and no device, go with the
108          * current device.
109          */
110         if (devspec == NULL || strchr(devspec, ':') == NULL) {
111                 if (((rv = devparse(dev, getenv("currdev"), NULL)) == 0) &&
112                     (path != NULL))
113                         *path = devspec;
114                 return (rv);
115         }
116
117         /*
118          * Try to parse the device name off the beginning of the devspec
119          */
120         return (devparse(dev, devspec, path));
121 }
122
123 static int
124 parse_args(int argc, const char **argv)
125 {
126         int howto = 0;
127
128         /*
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
138          * there as well.
139          */
140         for (int i = 1; i < argc; i++) {
141                 if (argv[i][0] != '\\' && argv[i][0] != '/') {
142                         howto |= boot_parse_arg(argv[i]);
143                 }
144         }
145
146         return (howto);
147 }
148
149 static vm_offset_t rsdp;
150
151 static vm_offset_t
152 kboot_rsdp_from_efi(void)
153 {
154         char buffer[512 + 1];
155         char *walker, *ep;
156
157         if (!file2str("/sys/firmware/efi/systab", buffer, sizeof(buffer)))
158                 return (0);     /* Not an EFI system */
159         ep = buffer + strlen(buffer);
160         walker = 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");
167         }
168         return (0);
169 }
170
171 static void
172 find_acpi(void)
173 {
174         rsdp = kboot_rsdp_from_efi();
175 #if 0   /* maybe for amd64 */
176         if (rsdp == 0)
177                 rsdp = find_rsdp_arch();
178 #endif
179 }
180
181 vm_offset_t
182 acpi_rsdp(void)
183 {
184         return (rsdp);
185 }
186
187 bool
188 has_acpi(void)
189 {
190         return rsdp != 0;
191 }
192
193 int
194 main(int argc, const char **argv)
195 {
196         void *heapbase;
197         const size_t heapsize = 64*1024*1024;
198         const char *bootdev;
199
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;
206
207         /* Give us a sane world if we're running as init */
208         do_init();
209
210         /*
211          * Setup the heap, 64MB is minimum for ZFS booting
212          */
213         heapbase = host_getmem(heapsize);
214         setheap(heapbase, heapbase + heapsize);
215
216         /* Parse the command line args -- ignoring for now the console selection */
217         parse_args(argc, argv);
218
219         /*
220          * Set up console.
221          */
222         cons_probe();
223
224         /* Initialize all the devices */
225         devinit();
226
227         bootdev = getenv("bootdev");
228         if (bootdev == NULL)
229                 bootdev = hostdisk_gen_probe();
230         if (bootdev == NULL)
231                 bootdev="zfs:";
232         hostfs_root = getenv("hostfs_root");
233         if (hostfs_root == NULL)
234                 hostfs_root = "/";
235 #if defined(LOADER_ZFS_SUPPORT)
236         if (strcmp(bootdev, "zfs:") == 0) {
237                 /*
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
241                  * on active BEs, etc
242                  */
243                 hostdisk_zfs_find_default();
244         } else
245 #endif
246         {
247                 /*
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.
252                  */
253                 set_currdev(bootdev);
254         }
255
256         printf("Boot device: %s with hostfs_root %s\n", bootdev, hostfs_root);
257
258         printf("\n%s", bootprog_info);
259
260         setenv("LINES", "24", 1);
261
262         memory_limits();
263         enumerate_memory_arch();
264
265         /*
266          * Find acpi, if it exists
267          */
268         find_acpi();
269
270         interact();                     /* doesn't return */
271
272         return (0);
273 }
274
275 void
276 exit(int code)
277 {
278         host_exit(code);
279         __unreachable();
280 }
281
282 void
283 delay(int usecs)
284 {
285         struct host_timeval tvi, tv;
286         uint64_t ti, t;
287         host_gettimeofday(&tvi, NULL);
288         ti = tvi.tv_sec*1000000 + tvi.tv_usec;
289         do {
290                 host_gettimeofday(&tv, NULL);
291                 t = tv.tv_sec*1000000 + tv.tv_usec;
292         } while (t < ti + usecs);
293 }
294
295 time_t
296 getsecs(void)
297 {
298         struct host_timeval tv;
299         host_gettimeofday(&tv, NULL);
300         return (tv.tv_sec);
301 }
302
303 time_t
304 time(time_t *tloc)
305 {
306         time_t rv;
307         
308         rv = getsecs();
309         if (tloc != NULL)
310                 *tloc = rv;
311
312         return (rv);
313 }
314
315 struct host_kexec_segment loaded_segments[HOST_KEXEC_SEGMENT_MAX];
316 int nkexec_segments = 0;
317
318 #define SEGALIGN (1ul<<20)
319
320 static ssize_t
321 get_phys_buffer(vm_offset_t dest, const size_t len, void **buf)
322 {
323         int i = 0;
324         const size_t segsize = 64*1024*1024;
325
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)
332                         goto out;
333         }
334
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;
339
340         i = nkexec_segments;
341         nkexec_segments++;
342
343 out:
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)));
348 }
349
350 ssize_t
351 kboot_copyin(const void *src, vm_offset_t dest, const size_t len)
352 {
353         ssize_t segsize, remainder;
354         void *destbuf;
355
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 */
359                 padding = 0;
360                 offset = dest;
361                 get_phys_buffer(pa_start, len, &destbuf);
362         }
363
364         remainder = len;
365         do {
366                 segsize = get_phys_buffer(dest + pa_start + padding - offset, remainder, &destbuf);
367                 bcopy(src, destbuf, segsize);
368                 remainder -= segsize;
369                 src += segsize;
370                 dest += segsize;
371         } while (remainder > 0);
372
373         return (len);
374 }
375
376 ssize_t
377 kboot_copyout(vm_offset_t src, void *dest, const size_t len)
378 {
379         ssize_t segsize, remainder;
380         void *srcbuf;
381
382         remainder = len;
383         do {
384                 segsize = get_phys_buffer(src + pa_start + padding - offset, remainder, &srcbuf);
385                 bcopy(srcbuf, dest, segsize);
386                 remainder -= segsize;
387                 src += segsize;
388                 dest += segsize;
389         } while (remainder > 0);
390
391         return (len);
392 }
393
394 ssize_t
395 kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len)
396 {
397         void            *buf;
398         size_t          resid, chunk, get;
399         ssize_t         got;
400         vm_offset_t     p;
401
402         p = dest;
403
404         chunk = min(PAGE_SIZE, len);
405         buf = malloc(chunk);
406         if (buf == NULL) {
407                 printf("kboot_readin: buf malloc failed\n");
408                 return (0);
409         }
410
411         for (resid = len; resid > 0; resid -= got, p += got) {
412                 get = min(chunk, resid);
413                 got = VECTX_READ(fd, buf, get);
414                 if (got <= 0) {
415                         if (got < 0)
416                                 printf("kboot_readin: read failed\n");
417                         break;
418                 }
419
420                 kboot_copyin(buf, p, got);
421         }
422
423         free (buf);
424         return (len - resid);
425 }
426
427 int
428 kboot_autoload(void)
429 {
430
431         return (0);
432 }
433
434 void
435 kboot_kseg_get(int *nseg, void **ptr)
436 {
437         int a;
438
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);
448         }
449
450         *nseg = nkexec_segments;
451         *ptr = &loaded_segments[0];
452 }
453
454 static void
455 kboot_zfs_probe(void)
456 {
457 #if defined(LOADER_ZFS_SUPPORT)
458         /*
459          * Open all the disks and partitions we can find to see if there are ZFS
460          * pools on them.
461          */
462         hostdisk_zfs_probe();
463 #endif
464 }
465
466 /*
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.
471  */
472 static int
473 command_fdt(int argc, char *argv[])
474 {
475
476         return (command_fdt_internal(argc, argv));
477 }
478         
479 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);