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