main.c revision 332150
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: stable/11/stand/powerpc/kboot/main.c 332150 2018-04-06 20:24:50Z kevans $");
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
40struct arch_switch	archsw;
41extern void *_end;
42
43extern char bootprog_info[];
44
45int kboot_getdev(void **vdev, const char *devspec, const char **path);
46ssize_t kboot_copyin(const void *src, vm_offset_t dest, const size_t len);
47ssize_t kboot_copyout(vm_offset_t src, void *dest, const size_t len);
48ssize_t kboot_readin(const int fd, vm_offset_t dest, const size_t len);
49int kboot_autoload(void);
50uint64_t kboot_loadaddr(u_int type, void *data, uint64_t addr);
51int kboot_setcurrdev(struct env_var *ev, int flags, const void *value);
52static void kboot_kseg_get(int *nseg, void **ptr);
53
54extern int command_fdt_internal(int argc, char *argv[]);
55
56struct region_desc {
57	uint64_t start;
58	uint64_t end;
59};
60
61static uint64_t
62kboot_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
191uint8_t
192kboot_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
216int
217kboot_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
238found:
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
255int
256main(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
301void
302exit(int code)
303{
304	while (1); /* XXX: host_exit */
305	__unreachable();
306}
307
308void
309delay(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
321time_t
322getsecs(void)
323{
324	struct host_timeval tv;
325	host_gettimeofday(&tv, NULL);
326	return (tv.tv_sec);
327}
328
329time_t
330time(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
341struct kexec_segment {
342	void *buf;
343	int bufsz;
344	void *mem;
345	int memsz;
346};
347
348struct kexec_segment loaded_segments[128];
349int nkexec_segments = 0;
350
351static ssize_t
352get_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
372out:
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
379ssize_t
380kboot_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
397ssize_t
398kboot_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
415ssize_t
416kboot_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
448int
449kboot_autoload(void)
450{
451
452	return (0);
453}
454
455uint64_t
456kboot_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
467static void
468kboot_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
486void
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 */
499static int
500command_fdt(int argc, char *argv[])
501{
502
503	return (command_fdt_internal(argc, argv));
504}
505
506COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
507
508