]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/kboot/main.c
netmap: Remove unused devclass arguments to DRIVER_MODULE.
[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/endian.h>
31 #include <sys/param.h>
32 #include <fdt_platform.h>
33
34 #include <machine/cpufunc.h>
35 #include <bootstrap.h>
36 #include "host_syscall.h"
37
38
39 struct arch_switch      archsw;
40 extern void *_end;
41
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 static void kboot_kseg_get(int *nseg, void **ptr);
49
50 extern int command_fdt_internal(int argc, char *argv[]);
51
52 struct region_desc {
53         uint64_t start;
54         uint64_t end;
55 };
56
57 static uint64_t
58 kboot_get_phys_load_segment(void)
59 {
60         int fd;
61         uint64_t entry[2];
62         static uint64_t load_segment = ~(0UL);
63         uint64_t val_64;
64         uint32_t val_32;
65         struct region_desc rsvd_reg[32];
66         int rsvd_reg_cnt = 0;
67         int ret, a, b;
68         uint64_t start, end;
69
70         if (load_segment == ~(0UL)) {
71
72                 /* Default load address is 0x00000000 */
73                 load_segment = 0UL;
74
75                 /* Read reserved regions */
76                 fd = host_open("/proc/device-tree/reserved-ranges", O_RDONLY, 0);
77                 if (fd >= 0) {
78                         while (host_read(fd, &entry[0], sizeof(entry)) == sizeof(entry)) {
79                                 rsvd_reg[rsvd_reg_cnt].start = be64toh(entry[0]);
80                                 rsvd_reg[rsvd_reg_cnt].end =
81                                     be64toh(entry[1]) + rsvd_reg[rsvd_reg_cnt].start - 1;
82                                 rsvd_reg_cnt++;
83                         }
84                         host_close(fd);
85                 }
86                 /* Read where the kernel ends */
87                 fd = host_open("/proc/device-tree/chosen/linux,kernel-end", O_RDONLY, 0);
88                 if (fd >= 0) {
89                         ret = host_read(fd, &val_64, sizeof(val_64));
90
91                         if (ret == sizeof(uint64_t)) {
92                                 rsvd_reg[rsvd_reg_cnt].start = 0;
93                                 rsvd_reg[rsvd_reg_cnt].end = be64toh(val_64) - 1;
94                         } else {
95                                 memcpy(&val_32, &val_64, sizeof(val_32));
96                                 rsvd_reg[rsvd_reg_cnt].start = 0;
97                                 rsvd_reg[rsvd_reg_cnt].end = be32toh(val_32) - 1;
98                         }
99                         rsvd_reg_cnt++;
100
101                         host_close(fd);
102                 }
103                 /* Read memory size (SOCKET0 only) */
104                 fd = host_open("/proc/device-tree/memory@0/reg", O_RDONLY, 0);
105                 if (fd < 0)
106                         fd = host_open("/proc/device-tree/memory/reg", O_RDONLY, 0);
107                 if (fd >= 0) {
108                         ret = host_read(fd, &entry, sizeof(entry));
109
110                         /* Memory range in start:length format */
111                         entry[0] = be64toh(entry[0]);
112                         entry[1] = be64toh(entry[1]);
113
114                         /* Reserve everything what is before start */
115                         if (entry[0] != 0) {
116                                 rsvd_reg[rsvd_reg_cnt].start = 0;
117                                 rsvd_reg[rsvd_reg_cnt].end = entry[0] - 1;
118                                 rsvd_reg_cnt++;
119                         }
120                         /* Reserve everything what is after end */
121                         if (entry[1] != 0xffffffffffffffffUL) {
122                                 rsvd_reg[rsvd_reg_cnt].start = entry[0] + entry[1];
123                                 rsvd_reg[rsvd_reg_cnt].end = 0xffffffffffffffffUL;
124                                 rsvd_reg_cnt++;
125                         }
126
127                         host_close(fd);
128                 }
129
130                 /* Sort entries in ascending order (bubble) */
131                 for (a = rsvd_reg_cnt - 1; a > 0; a--) {
132                         for (b = 0; b < a; b++) {
133                                 if (rsvd_reg[b].start > rsvd_reg[b + 1].start) {
134                                         struct region_desc tmp;
135                                         tmp = rsvd_reg[b];
136                                         rsvd_reg[b] = rsvd_reg[b + 1];
137                                         rsvd_reg[b + 1] = tmp;
138                                 }
139                         }
140                 }
141
142                 /* Join overlapping/adjacent regions */
143                 for (a = 0; a < rsvd_reg_cnt - 1; ) {
144
145                         if ((rsvd_reg[a + 1].start >= rsvd_reg[a].start) &&
146                             ((rsvd_reg[a + 1].start - 1) <= rsvd_reg[a].end)) {
147                                 /* We have overlapping/adjacent regions! */
148                                 rsvd_reg[a].end =
149                                     MAX(rsvd_reg[a].end, rsvd_reg[a + a].end);
150
151                                 for (b = a + 1; b < rsvd_reg_cnt - 1; b++)
152                                         rsvd_reg[b] = rsvd_reg[b + 1];
153                                 rsvd_reg_cnt--;
154                         } else
155                                 a++;
156                 }
157
158                 /* Find the first free region */
159                 if (rsvd_reg_cnt > 0) {
160                         start = 0;
161                         end = rsvd_reg[0].start;
162                         for (a = 0; a < rsvd_reg_cnt - 1; a++) {
163                                 if ((start >= rsvd_reg[a].start) &&
164                                     (start <= rsvd_reg[a].end)) {
165                                         start = rsvd_reg[a].end + 1;
166                                         end = rsvd_reg[a + 1].start;
167                                 } else
168                                         break;
169                         }
170
171                         if (start != end) {
172                                 uint64_t align = 64UL*1024UL*1024UL;
173
174                                 /* Align both to 64MB boundary */
175                                 start = (start + align - 1UL) & ~(align - 1UL);
176                                 end = ((end + 1UL) & ~(align - 1UL)) - 1UL;
177
178                                 if (start < end)
179                                         load_segment = start;
180                         }
181                 }
182         }
183
184         return (load_segment);
185 }
186
187 uint8_t
188 kboot_get_kernel_machine_bits(void)
189 {
190         static uint8_t bits = 0;
191         struct old_utsname utsname;
192         int ret;
193
194         if (bits == 0) {
195                 /* Default is 32-bit kernel */
196                 bits = 32;
197
198                 /* Try to get system type */
199                 memset(&utsname, 0, sizeof(utsname));
200                 ret = host_uname(&utsname);
201                 if (ret == 0) {
202                         if (strcmp(utsname.machine, "ppc64") == 0)
203                                 bits = 64;
204                         else if (strcmp(utsname.machine, "ppc64le") == 0)
205                                 bits = 64;
206                 }
207         }
208
209         return (bits);
210 }
211
212 int
213 kboot_getdev(void **vdev, const char *devspec, const char **path)
214 {
215         int i;
216         const char *devpath, *filepath;
217         struct devsw *dv;
218         struct devdesc *desc;
219
220         if (strchr(devspec, ':') != NULL) {
221                 devpath = devspec;
222                 filepath = strchr(devspec, ':') + 1;
223         } else {
224                 devpath = getenv("currdev");
225                 filepath = devspec;
226         }
227
228         for (i = 0; (dv = devsw[i]) != NULL; i++) {
229                 if (strncmp(dv->dv_name, devpath, strlen(dv->dv_name)) == 0)
230                         goto found;
231         }
232         return (ENOENT);
233
234 found:
235         if (path != NULL && filepath != NULL)
236                 *path = filepath;
237         else if (path != NULL)
238                 *path = strchr(devspec, ':') + 1;
239
240         if (vdev != NULL) {
241                 desc = malloc(sizeof(*desc));
242                 desc->d_dev = dv;
243                 desc->d_unit = 0;
244                 desc->d_opendata = strdup(devpath);
245                 *vdev = desc;
246         }
247
248         return (0);
249 }
250
251 int
252 main(int argc, const char **argv)
253 {
254         void *heapbase;
255         const size_t heapsize = 15*1024*1024;
256         const char *bootdev;
257
258         /*
259          * Set the heap to one page after the end of the loader.
260          */
261         heapbase = host_getmem(heapsize);
262         setheap(heapbase, heapbase + heapsize);
263
264         /*
265          * Set up console.
266          */
267         cons_probe();
268
269         /* Choose bootdev if provided */
270         if (argc > 1)
271                 bootdev = argv[1];
272         else
273                 bootdev = "";
274
275         printf("Boot device: %s\n", bootdev);
276
277         archsw.arch_getdev = kboot_getdev;
278         archsw.arch_copyin = kboot_copyin;
279         archsw.arch_copyout = kboot_copyout;
280         archsw.arch_readin = kboot_readin;
281         archsw.arch_autoload = kboot_autoload;
282         archsw.arch_loadaddr = kboot_loadaddr;
283         archsw.arch_kexec_kseg_get = kboot_kseg_get;
284
285         printf("\n%s", bootprog_info);
286
287         setenv("currdev", bootdev, 1);
288         setenv("loaddev", bootdev, 1);
289         setenv("LINES", "24", 1);
290         setenv("usefdt", "1", 1);
291
292         interact();                     /* doesn't return */
293
294         return (0);
295 }
296
297 void
298 exit(int code)
299 {
300         while (1); /* XXX: host_exit */
301         __unreachable();
302 }
303
304 void
305 delay(int usecs)
306 {
307         struct host_timeval tvi, tv;
308         uint64_t ti, t;
309         host_gettimeofday(&tvi, NULL);
310         ti = tvi.tv_sec*1000000 + tvi.tv_usec;
311         do {
312                 host_gettimeofday(&tv, NULL);
313                 t = tv.tv_sec*1000000 + tv.tv_usec;
314         } while (t < ti + usecs);
315 }
316
317 time_t
318 getsecs(void)
319 {
320         struct host_timeval tv;
321         host_gettimeofday(&tv, NULL);
322         return (tv.tv_sec);
323 }
324
325 time_t
326 time(time_t *tloc)
327 {
328         time_t rv;
329         
330         rv = getsecs();
331         if (tloc != NULL)
332                 *tloc = rv;
333
334         return (rv);
335 }
336
337 struct kexec_segment {
338         void *buf;
339         int bufsz;
340         void *mem;
341         int memsz;
342 };
343
344 struct kexec_segment loaded_segments[128];
345 int nkexec_segments = 0;
346
347 static ssize_t
348 get_phys_buffer(vm_offset_t dest, const size_t len, void **buf)
349 {
350         int i = 0;
351         const size_t segsize = 4*1024*1024;
352
353         for (i = 0; i < nkexec_segments; i++) {
354                 if (dest >= (vm_offset_t)loaded_segments[i].mem &&
355                     dest < (vm_offset_t)loaded_segments[i].mem +
356                     loaded_segments[i].memsz)
357                         goto out;
358         }
359
360         loaded_segments[nkexec_segments].buf = host_getmem(segsize);
361         loaded_segments[nkexec_segments].bufsz = segsize;
362         loaded_segments[nkexec_segments].mem = (void *)rounddown2(dest,segsize);
363         loaded_segments[nkexec_segments].memsz = segsize;
364
365         i = nkexec_segments;
366         nkexec_segments++;
367
368 out:
369         *buf = loaded_segments[i].buf + (dest -
370             (vm_offset_t)loaded_segments[i].mem);
371         return (min(len,loaded_segments[i].bufsz - (dest -
372             (vm_offset_t)loaded_segments[i].mem)));
373 }
374
375 ssize_t
376 kboot_copyin(const void *src, vm_offset_t dest, const size_t len)
377 {
378         ssize_t segsize, remainder;
379         void *destbuf;
380
381         remainder = len;
382         do {
383                 segsize = get_phys_buffer(dest, remainder, &destbuf);
384                 bcopy(src, destbuf, segsize);
385                 remainder -= segsize;
386                 src += segsize;
387                 dest += segsize;
388         } while (remainder > 0);
389
390         return (len);
391 }
392
393 ssize_t
394 kboot_copyout(vm_offset_t src, void *dest, const size_t len)
395 {
396         ssize_t segsize, remainder;
397         void *srcbuf;
398
399         remainder = len;
400         do {
401                 segsize = get_phys_buffer(src, remainder, &srcbuf);
402                 bcopy(srcbuf, dest, segsize);
403                 remainder -= segsize;
404                 src += segsize;
405                 dest += segsize;
406         } while (remainder > 0);
407
408         return (len);
409 }
410
411 ssize_t
412 kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len)
413 {
414         void            *buf;
415         size_t          resid, chunk, get;
416         ssize_t         got;
417         vm_offset_t     p;
418
419         p = dest;
420
421         chunk = min(PAGE_SIZE, len);
422         buf = malloc(chunk);
423         if (buf == NULL) {
424                 printf("kboot_readin: buf malloc failed\n");
425                 return (0);
426         }
427
428         for (resid = len; resid > 0; resid -= got, p += got) {
429                 get = min(chunk, resid);
430                 got = VECTX_READ(fd, buf, get);
431                 if (got <= 0) {
432                         if (got < 0)
433                                 printf("kboot_readin: read failed\n");
434                         break;
435                 }
436
437                 kboot_copyin(buf, p, got);
438         }
439
440         free (buf);
441         return (len - resid);
442 }
443
444 int
445 kboot_autoload(void)
446 {
447
448         return (0);
449 }
450
451 uint64_t
452 kboot_loadaddr(u_int type, void *data, uint64_t addr)
453 {
454
455         if (type == LOAD_ELF)
456                 addr = roundup(addr, PAGE_SIZE);
457         else
458                 addr += kboot_get_phys_load_segment();
459
460         return (addr);
461 }
462
463 static void
464 kboot_kseg_get(int *nseg, void **ptr)
465 {
466 #if 0
467         int a;
468
469         for (a = 0; a < nkexec_segments; a++) {
470                 printf("kseg_get: %jx %jx %jx %jx\n",
471                         (uintmax_t)loaded_segments[a].buf,
472                         (uintmax_t)loaded_segments[a].bufsz,
473                         (uintmax_t)loaded_segments[a].mem,
474                         (uintmax_t)loaded_segments[a].memsz);
475         }
476 #endif
477
478         *nseg = nkexec_segments;
479         *ptr = &loaded_segments[0];
480 }
481
482 void
483 _start(int argc, const char **argv, char **env)
484 {
485         main(argc, argv);
486 }
487
488 /*
489  * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
490  * and declaring it as extern is in contradiction with COMMAND_SET() macro
491  * (which uses static pointer), we're defining wrapper function, which
492  * calls the proper fdt handling routine.
493  */
494 static int
495 command_fdt(int argc, char *argv[])
496 {
497
498         return (command_fdt_internal(argc, argv));
499 }
500         
501 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);