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