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