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