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