1091c255bSWarner Losh /*-
2091c255bSWarner Losh  * Copyright (C) 2010-2014 Nathan Whitehorn
3091c255bSWarner Losh  * All rights reserved.
4091c255bSWarner Losh  *
5091c255bSWarner Losh  * Redistribution and use in source and binary forms, with or without
6091c255bSWarner Losh  * modification, are permitted provided that the following conditions
7091c255bSWarner Losh  * are met:
8091c255bSWarner Losh  * 1. Redistributions of source code must retain the above copyright
9091c255bSWarner Losh  *    notice, this list of conditions and the following disclaimer.
10091c255bSWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
11091c255bSWarner Losh  *    notice, this list of conditions and the following disclaimer in the
12091c255bSWarner Losh  *    documentation and/or other materials provided with the distribution.
13091c255bSWarner Losh  *
14091c255bSWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15091c255bSWarner Losh  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16091c255bSWarner Losh  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17091c255bSWarner Losh  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18091c255bSWarner Losh  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19091c255bSWarner Losh  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20091c255bSWarner Losh  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21091c255bSWarner Losh  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22091c255bSWarner Losh  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23091c255bSWarner Losh  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24091c255bSWarner Losh  */
25091c255bSWarner Losh 
26091c255bSWarner Losh #include <sys/param.h>
27091c255bSWarner Losh #include <sys/endian.h>
28091c255bSWarner Losh 
29091c255bSWarner Losh #include "stand.h"
30091c255bSWarner Losh #include "host_syscall.h"
31091c255bSWarner Losh #include "kboot.h"
32091c255bSWarner Losh 
33091c255bSWarner Losh struct region_desc {
34091c255bSWarner Losh 	uint64_t start;
35091c255bSWarner Losh 	uint64_t end;
36091c255bSWarner Losh };
37091c255bSWarner Losh 
38091c255bSWarner Losh /*
39091c255bSWarner Losh  * Find a good place to load the kernel, subject to the PowerPC's constraints
40091c255bSWarner Losh  *
41091c255bSWarner Losh  * This excludes ranges that are marked as reserved.
42091c255bSWarner Losh  * And 0..end of kernel
43091c255bSWarner Losh  *
44091c255bSWarner Losh  * It then tries to find the memory exposed from the DTB, which it assumes is one
45091c255bSWarner Losh  * contiguous range. It adds everything not in that list to the excluded list.
46091c255bSWarner Losh  *
47091c255bSWarner Losh  * Sort, dedup, and it finds the first region and uses that as the load_segment
48091c255bSWarner Losh  * and returns that. All addresses are offset by this amount.
49091c255bSWarner Losh  */
50091c255bSWarner Losh uint64_t
kboot_get_phys_load_segment(void)51091c255bSWarner Losh kboot_get_phys_load_segment(void)
52091c255bSWarner Losh {
53091c255bSWarner Losh 	int fd;
54091c255bSWarner Losh 	uint64_t entry[2];
55091c255bSWarner Losh 	static uint64_t load_segment = ~(0UL);
56091c255bSWarner Losh 	uint64_t val_64;
57091c255bSWarner Losh 	uint32_t val_32;
58091c255bSWarner Losh 	struct region_desc rsvd_reg[32];
59091c255bSWarner Losh 	int rsvd_reg_cnt = 0;
60091c255bSWarner Losh 	int ret, a, b;
61091c255bSWarner Losh 	uint64_t start, end;
62091c255bSWarner Losh 
63091c255bSWarner Losh 	if (load_segment == ~(0UL)) {
64091c255bSWarner Losh 
65091c255bSWarner Losh 		/* Default load address is 0x00000000 */
66091c255bSWarner Losh 		load_segment = 0UL;
67091c255bSWarner Losh 
68091c255bSWarner Losh 		/* Read reserved regions */
69091c255bSWarner Losh 		fd = host_open("/proc/device-tree/reserved-ranges", O_RDONLY, 0);
70091c255bSWarner Losh 		if (fd >= 0) {
71091c255bSWarner Losh 			while (host_read(fd, &entry[0], sizeof(entry)) == sizeof(entry)) {
72091c255bSWarner Losh 				rsvd_reg[rsvd_reg_cnt].start = be64toh(entry[0]);
73091c255bSWarner Losh 				rsvd_reg[rsvd_reg_cnt].end =
74091c255bSWarner Losh 				    be64toh(entry[1]) + rsvd_reg[rsvd_reg_cnt].start - 1;
75091c255bSWarner Losh 				rsvd_reg_cnt++;
76091c255bSWarner Losh 			}
77091c255bSWarner Losh 			host_close(fd);
78091c255bSWarner Losh 		}
79091c255bSWarner Losh 		/* Read where the kernel ends */
80091c255bSWarner Losh 		fd = host_open("/proc/device-tree/chosen/linux,kernel-end", O_RDONLY, 0);
81091c255bSWarner Losh 		if (fd >= 0) {
82091c255bSWarner Losh 			ret = host_read(fd, &val_64, sizeof(val_64));
83091c255bSWarner Losh 
84091c255bSWarner Losh 			if (ret == sizeof(uint64_t)) {
85091c255bSWarner Losh 				rsvd_reg[rsvd_reg_cnt].start = 0;
86091c255bSWarner Losh 				rsvd_reg[rsvd_reg_cnt].end = be64toh(val_64) - 1;
87091c255bSWarner Losh 			} else {
88091c255bSWarner Losh 				memcpy(&val_32, &val_64, sizeof(val_32));
89091c255bSWarner Losh 				rsvd_reg[rsvd_reg_cnt].start = 0;
90091c255bSWarner Losh 				rsvd_reg[rsvd_reg_cnt].end = be32toh(val_32) - 1;
91091c255bSWarner Losh 			}
92091c255bSWarner Losh 			rsvd_reg_cnt++;
93091c255bSWarner Losh 
94091c255bSWarner Losh 			host_close(fd);
95091c255bSWarner Losh 		}
96091c255bSWarner Losh 		/* Read memory size (SOCKET0 only) */
97091c255bSWarner Losh 		fd = host_open("/proc/device-tree/memory@0/reg", O_RDONLY, 0);
98091c255bSWarner Losh 		if (fd < 0)
99091c255bSWarner Losh 			fd = host_open("/proc/device-tree/memory/reg", O_RDONLY, 0);
100091c255bSWarner Losh 		if (fd >= 0) {
101091c255bSWarner Losh 			ret = host_read(fd, &entry, sizeof(entry));
102091c255bSWarner Losh 
103091c255bSWarner Losh 			/* Memory range in start:length format */
104091c255bSWarner Losh 			entry[0] = be64toh(entry[0]);
105091c255bSWarner Losh 			entry[1] = be64toh(entry[1]);
106091c255bSWarner Losh 
107091c255bSWarner Losh 			/* Reserve everything what is before start */
108091c255bSWarner Losh 			if (entry[0] != 0) {
109091c255bSWarner Losh 				rsvd_reg[rsvd_reg_cnt].start = 0;
110091c255bSWarner Losh 				rsvd_reg[rsvd_reg_cnt].end = entry[0] - 1;
111091c255bSWarner Losh 				rsvd_reg_cnt++;
112091c255bSWarner Losh 			}
113091c255bSWarner Losh 			/* Reserve everything what is after end */
114091c255bSWarner Losh 			if (entry[1] != 0xffffffffffffffffUL) {
115091c255bSWarner Losh 				rsvd_reg[rsvd_reg_cnt].start = entry[0] + entry[1];
116091c255bSWarner Losh 				rsvd_reg[rsvd_reg_cnt].end = 0xffffffffffffffffUL;
117091c255bSWarner Losh 				rsvd_reg_cnt++;
118091c255bSWarner Losh 			}
119091c255bSWarner Losh 
120091c255bSWarner Losh 			host_close(fd);
121091c255bSWarner Losh 		}
122091c255bSWarner Losh 
123091c255bSWarner Losh 		/* Sort entries in ascending order (bubble) */
124091c255bSWarner Losh 		for (a = rsvd_reg_cnt - 1; a > 0; a--) {
125091c255bSWarner Losh 			for (b = 0; b < a; b++) {
126091c255bSWarner Losh 				if (rsvd_reg[b].start > rsvd_reg[b + 1].start) {
127091c255bSWarner Losh 					struct region_desc tmp;
128091c255bSWarner Losh 					tmp = rsvd_reg[b];
129091c255bSWarner Losh 					rsvd_reg[b] = rsvd_reg[b + 1];
130091c255bSWarner Losh 					rsvd_reg[b + 1] = tmp;
131091c255bSWarner Losh 				}
132091c255bSWarner Losh 			}
133091c255bSWarner Losh 		}
134091c255bSWarner Losh 
135091c255bSWarner Losh 		/* Join overlapping/adjacent regions */
136091c255bSWarner Losh 		for (a = 0; a < rsvd_reg_cnt - 1; ) {
137091c255bSWarner Losh 
138091c255bSWarner Losh 			if ((rsvd_reg[a + 1].start >= rsvd_reg[a].start) &&
139091c255bSWarner Losh 			    ((rsvd_reg[a + 1].start - 1) <= rsvd_reg[a].end)) {
140091c255bSWarner Losh 				/* We have overlapping/adjacent regions! */
141091c255bSWarner Losh 				rsvd_reg[a].end =
142091c255bSWarner Losh 				    MAX(rsvd_reg[a].end, rsvd_reg[a + a].end);
143091c255bSWarner Losh 
144091c255bSWarner Losh 				for (b = a + 1; b < rsvd_reg_cnt - 1; b++)
145091c255bSWarner Losh 					rsvd_reg[b] = rsvd_reg[b + 1];
146091c255bSWarner Losh 				rsvd_reg_cnt--;
147091c255bSWarner Losh 			} else
148091c255bSWarner Losh 				a++;
149091c255bSWarner Losh 		}
150091c255bSWarner Losh 
151091c255bSWarner Losh 		/* Find the first free region */
152091c255bSWarner Losh 		if (rsvd_reg_cnt > 0) {
153091c255bSWarner Losh 			start = 0;
154091c255bSWarner Losh 			end = rsvd_reg[0].start;
155091c255bSWarner Losh 			for (a = 0; a < rsvd_reg_cnt - 1; a++) {
156091c255bSWarner Losh 				if ((start >= rsvd_reg[a].start) &&
157091c255bSWarner Losh 				    (start <= rsvd_reg[a].end)) {
158091c255bSWarner Losh 					start = rsvd_reg[a].end + 1;
159091c255bSWarner Losh 					end = rsvd_reg[a + 1].start;
160091c255bSWarner Losh 				} else
161091c255bSWarner Losh 					break;
162091c255bSWarner Losh 			}
163091c255bSWarner Losh 
164091c255bSWarner Losh 			if (start != end) {
165091c255bSWarner Losh 				uint64_t align = 64UL*1024UL*1024UL;
166091c255bSWarner Losh 
167091c255bSWarner Losh 				/* Align both to 64MB boundary */
168091c255bSWarner Losh 				start = (start + align - 1UL) & ~(align - 1UL);
169091c255bSWarner Losh 				end = ((end + 1UL) & ~(align - 1UL)) - 1UL;
170091c255bSWarner Losh 
171091c255bSWarner Losh 				if (start < end)
172091c255bSWarner Losh 					load_segment = start;
173091c255bSWarner Losh 			}
174091c255bSWarner Losh 		}
175091c255bSWarner Losh 	}
176091c255bSWarner Losh 
177091c255bSWarner Losh 	return (load_segment);
178091c255bSWarner Losh }
179091c255bSWarner Losh 
180091c255bSWarner Losh #if 0
181091c255bSWarner Losh /*
182091c255bSWarner Losh  * XXX this appears to be unused, but may have been for selecting the allowed
183091c255bSWarner Losh  * kernels ABIs. It's been unused since the first commit, which suggests an
184091c255bSWarner Losh  * error in bringing this into the tree.
185091c255bSWarner Losh  */
186091c255bSWarner Losh uint8_t
187091c255bSWarner Losh kboot_get_kernel_machine_bits(void)
188091c255bSWarner Losh {
189091c255bSWarner Losh 	static uint8_t bits = 0;
190091c255bSWarner Losh 	struct old_utsname utsname;
191091c255bSWarner Losh 	int ret;
192091c255bSWarner Losh 
193091c255bSWarner Losh 	if (bits == 0) {
194091c255bSWarner Losh 		/* Default is 32-bit kernel */
195091c255bSWarner Losh 		bits = 32;
196091c255bSWarner Losh 
197091c255bSWarner Losh 		/* Try to get system type */
198091c255bSWarner Losh 		memset(&utsname, 0, sizeof(utsname));
199091c255bSWarner Losh 		ret = host_uname(&utsname);
200091c255bSWarner Losh 		if (ret == 0) {
201091c255bSWarner Losh 			if (strcmp(utsname.machine, "ppc64") == 0)
202091c255bSWarner Losh 				bits = 64;
203091c255bSWarner Losh 			else if (strcmp(utsname.machine, "ppc64le") == 0)
204091c255bSWarner Losh 				bits = 64;
205091c255bSWarner Losh 		}
206091c255bSWarner Losh 	}
207091c255bSWarner Losh 
208091c255bSWarner Losh 	return (bits);
209091c255bSWarner Losh }
210091c255bSWarner Losh #endif
211091c255bSWarner Losh 
212091c255bSWarner Losh /* Need to transition from current hacky FDT way to this code */
enumerate_memory_arch(void)213091c255bSWarner Losh bool enumerate_memory_arch(void)
214091c255bSWarner Losh {
215091c255bSWarner Losh 	/*
216091c255bSWarner Losh 	 * For now, we dig it out of the FDT, plus we need to pass all data into
217091c255bSWarner Losh 	 * the kernel via the (adjusted) FDT we find.
218091c255bSWarner Losh 	 */
219091c255bSWarner Losh 	setenv("usefdt", "1", 1);
220091c255bSWarner Losh 
221091c255bSWarner Losh 	return true;
222091c255bSWarner Losh }
223091c255bSWarner Losh 
224091c255bSWarner Losh void
bi_loadsmap(struct preloaded_file * kfp)225091c255bSWarner Losh bi_loadsmap(struct preloaded_file *kfp)
226091c255bSWarner Losh {
227091c255bSWarner Losh 	/* passed in via the DTB */
228091c255bSWarner Losh }
229