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/param.h>
27 #include <sys/endian.h>
28 
29 #include "stand.h"
30 #include "host_syscall.h"
31 #include "kboot.h"
32 
33 struct region_desc {
34 	uint64_t start;
35 	uint64_t end;
36 };
37 
38 /*
39  * Find a good place to load the kernel, subject to the PowerPC's constraints
40  *
41  * This excludes ranges that are marked as reserved.
42  * And 0..end of kernel
43  *
44  * It then tries to find the memory exposed from the DTB, which it assumes is one
45  * contiguous range. It adds everything not in that list to the excluded list.
46  *
47  * Sort, dedup, and it finds the first region and uses that as the load_segment
48  * and returns that. All addresses are offset by this amount.
49  */
50 uint64_t
51 kboot_get_phys_load_segment(void)
52 {
53 	int fd;
54 	uint64_t entry[2];
55 	static uint64_t load_segment = ~(0UL);
56 	uint64_t val_64;
57 	uint32_t val_32;
58 	struct region_desc rsvd_reg[32];
59 	int rsvd_reg_cnt = 0;
60 	int ret, a, b;
61 	uint64_t start, end;
62 
63 	if (load_segment == ~(0UL)) {
64 
65 		/* Default load address is 0x00000000 */
66 		load_segment = 0UL;
67 
68 		/* Read reserved regions */
69 		fd = host_open("/proc/device-tree/reserved-ranges", O_RDONLY, 0);
70 		if (fd >= 0) {
71 			while (host_read(fd, &entry[0], sizeof(entry)) == sizeof(entry)) {
72 				rsvd_reg[rsvd_reg_cnt].start = be64toh(entry[0]);
73 				rsvd_reg[rsvd_reg_cnt].end =
74 				    be64toh(entry[1]) + rsvd_reg[rsvd_reg_cnt].start - 1;
75 				rsvd_reg_cnt++;
76 			}
77 			host_close(fd);
78 		}
79 		/* Read where the kernel ends */
80 		fd = host_open("/proc/device-tree/chosen/linux,kernel-end", O_RDONLY, 0);
81 		if (fd >= 0) {
82 			ret = host_read(fd, &val_64, sizeof(val_64));
83 
84 			if (ret == sizeof(uint64_t)) {
85 				rsvd_reg[rsvd_reg_cnt].start = 0;
86 				rsvd_reg[rsvd_reg_cnt].end = be64toh(val_64) - 1;
87 			} else {
88 				memcpy(&val_32, &val_64, sizeof(val_32));
89 				rsvd_reg[rsvd_reg_cnt].start = 0;
90 				rsvd_reg[rsvd_reg_cnt].end = be32toh(val_32) - 1;
91 			}
92 			rsvd_reg_cnt++;
93 
94 			host_close(fd);
95 		}
96 		/* Read memory size (SOCKET0 only) */
97 		fd = host_open("/proc/device-tree/memory@0/reg", O_RDONLY, 0);
98 		if (fd < 0)
99 			fd = host_open("/proc/device-tree/memory/reg", O_RDONLY, 0);
100 		if (fd >= 0) {
101 			ret = host_read(fd, &entry, sizeof(entry));
102 
103 			/* Memory range in start:length format */
104 			entry[0] = be64toh(entry[0]);
105 			entry[1] = be64toh(entry[1]);
106 
107 			/* Reserve everything what is before start */
108 			if (entry[0] != 0) {
109 				rsvd_reg[rsvd_reg_cnt].start = 0;
110 				rsvd_reg[rsvd_reg_cnt].end = entry[0] - 1;
111 				rsvd_reg_cnt++;
112 			}
113 			/* Reserve everything what is after end */
114 			if (entry[1] != 0xffffffffffffffffUL) {
115 				rsvd_reg[rsvd_reg_cnt].start = entry[0] + entry[1];
116 				rsvd_reg[rsvd_reg_cnt].end = 0xffffffffffffffffUL;
117 				rsvd_reg_cnt++;
118 			}
119 
120 			host_close(fd);
121 		}
122 
123 		/* Sort entries in ascending order (bubble) */
124 		for (a = rsvd_reg_cnt - 1; a > 0; a--) {
125 			for (b = 0; b < a; b++) {
126 				if (rsvd_reg[b].start > rsvd_reg[b + 1].start) {
127 					struct region_desc tmp;
128 					tmp = rsvd_reg[b];
129 					rsvd_reg[b] = rsvd_reg[b + 1];
130 					rsvd_reg[b + 1] = tmp;
131 				}
132 			}
133 		}
134 
135 		/* Join overlapping/adjacent regions */
136 		for (a = 0; a < rsvd_reg_cnt - 1; ) {
137 
138 			if ((rsvd_reg[a + 1].start >= rsvd_reg[a].start) &&
139 			    ((rsvd_reg[a + 1].start - 1) <= rsvd_reg[a].end)) {
140 				/* We have overlapping/adjacent regions! */
141 				rsvd_reg[a].end =
142 				    MAX(rsvd_reg[a].end, rsvd_reg[a + a].end);
143 
144 				for (b = a + 1; b < rsvd_reg_cnt - 1; b++)
145 					rsvd_reg[b] = rsvd_reg[b + 1];
146 				rsvd_reg_cnt--;
147 			} else
148 				a++;
149 		}
150 
151 		/* Find the first free region */
152 		if (rsvd_reg_cnt > 0) {
153 			start = 0;
154 			end = rsvd_reg[0].start;
155 			for (a = 0; a < rsvd_reg_cnt - 1; a++) {
156 				if ((start >= rsvd_reg[a].start) &&
157 				    (start <= rsvd_reg[a].end)) {
158 					start = rsvd_reg[a].end + 1;
159 					end = rsvd_reg[a + 1].start;
160 				} else
161 					break;
162 			}
163 
164 			if (start != end) {
165 				uint64_t align = 64UL*1024UL*1024UL;
166 
167 				/* Align both to 64MB boundary */
168 				start = (start + align - 1UL) & ~(align - 1UL);
169 				end = ((end + 1UL) & ~(align - 1UL)) - 1UL;
170 
171 				if (start < end)
172 					load_segment = start;
173 			}
174 		}
175 	}
176 
177 	return (load_segment);
178 }
179 
180 #if 0
181 /*
182  * XXX this appears to be unused, but may have been for selecting the allowed
183  * kernels ABIs. It's been unused since the first commit, which suggests an
184  * error in bringing this into the tree.
185  */
186 uint8_t
187 kboot_get_kernel_machine_bits(void)
188 {
189 	static uint8_t bits = 0;
190 	struct old_utsname utsname;
191 	int ret;
192 
193 	if (bits == 0) {
194 		/* Default is 32-bit kernel */
195 		bits = 32;
196 
197 		/* Try to get system type */
198 		memset(&utsname, 0, sizeof(utsname));
199 		ret = host_uname(&utsname);
200 		if (ret == 0) {
201 			if (strcmp(utsname.machine, "ppc64") == 0)
202 				bits = 64;
203 			else if (strcmp(utsname.machine, "ppc64le") == 0)
204 				bits = 64;
205 		}
206 	}
207 
208 	return (bits);
209 }
210 #endif
211 
212 /* Need to transition from current hacky FDT way to this code */
213 bool enumerate_memory_arch(void)
214 {
215 	/*
216 	 * For now, we dig it out of the FDT, plus we need to pass all data into
217 	 * the kernel via the (adjusted) FDT we find.
218 	 */
219 	setenv("usefdt", "1", 1);
220 
221 	return true;
222 }
223 
224 void
225 bi_loadsmap(struct preloaded_file *kfp)
226 {
227 	/* passed in via the DTB */
228 }
229