1 /* $NetBSD: loadfile_machdep.c,v 1.2 2022/07/10 14:18:27 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2021 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "boot.h" 33 34 #include <sys/param.h> 35 #include <sys/boot_flag.h> 36 #include <sys/disklabel.h> 37 38 #include <lib/libsa/stand.h> 39 #include <lib/libsa/loadfile.h> 40 #include <lib/libkern/libkern.h> 41 42 #include "openfirm.h" 43 44 #ifdef KMAPPING_DEBUG 45 #define DPRINTF printf 46 #else 47 #define DPRINTF while (0) printf 48 #endif 49 50 static int 51 ofw_claimphys(paddr_t pa, vsize_t size) 52 { 53 /* (2) phys, (2) size, (1) align -> 2 (phys) */ 54 uint32_t cells[2+2+1+2]; 55 uint32_t *p = cells; 56 paddr_t result; 57 58 if (ofw_address_cells == 2) { 59 *p++ = ((uint64_t)pa) >> 32; 60 *p++ = (uint32_t)pa; 61 } else { 62 *p++ = (uint32_t)pa; 63 } 64 65 #if 0 /* No known Mac systems with 2. */ 66 if (ofw_size_cells == 2) { 67 *p++ = ((uint64_t)size) >> 32; 68 *p++ = (uint32_t)size; 69 } else 70 #endif 71 *p++ = (uint32_t)size; 72 73 *p++ = 0; /* align */ 74 75 if (OF_call_method("claim", ofw_memory_ihandle, 76 ofw_address_cells + ofw_size_cells + 1, 77 ofw_address_cells, (int *)cells) == -1) { 78 return -1; 79 } 80 81 if (ofw_address_cells == 2) { 82 uint64_t v; 83 v = (uint64_t)(*p++) << 32; 84 v |= *p++; 85 result = (paddr_t)v; 86 } else { 87 result = *p++; 88 } 89 90 if (result != pa) { 91 printf("!!! CLAIM PHYS 0x%lx RETURNED 0x%lx\n", 92 pa, result); 93 return -1; 94 } 95 96 return 0; 97 } 98 99 static int 100 ofw_releasephys(paddr_t pa, vsize_t size) 101 { 102 /* (2) phys, (2) size, -> nil */ 103 uint32_t cells[2+2]; 104 uint32_t *p = cells; 105 106 if (ofw_address_cells == 2) { 107 *p++ = ((uint64_t)pa) >> 32; 108 *p++ = (uint32_t)pa; 109 } else { 110 *p++ = (uint32_t)pa; 111 } 112 113 #if 0 /* No known Mac systems with 2. */ 114 if (ofw_size_cells == 2) { 115 *p++ = ((uint64_t)size) >> 32; 116 *p++ = (uint32_t)size; 117 } else 118 #endif 119 *p++ = (uint32_t)size; 120 121 if (OF_call_method("release", ofw_memory_ihandle, 122 ofw_address_cells + ofw_size_cells, 123 0, (int *)cells) == -1) { 124 return -1; 125 } 126 127 return 0; 128 } 129 130 static int 131 ofw_claimvirt(vaddr_t va, vsize_t size) 132 { 133 /* (1) virt, (1) size, (1) align -> (1) virt */ 134 uint32_t cells[1+1+1+1]; 135 uint32_t *p = cells; 136 vaddr_t result; 137 138 *p++ = va; 139 *p++ = size; 140 *p++ = 0; /* align */ 141 142 if (OF_call_method("claim", ofw_mmu_ihandle, 143 3, 1, (int *)cells) == -1) { 144 return -1; 145 } 146 147 result = *p++; 148 149 if (result != va) { 150 printf("!!! CLAIM VIRT 0x%lx RETURNED 0x%lx\n", 151 va, result); 152 return -1; 153 } 154 155 return 0; 156 } 157 158 static int 159 ofw_releasevirt(vaddr_t va, vsize_t size) 160 { 161 /* (1) virt, (1) size -> nil */ 162 uint32_t cells[1+1]; 163 uint32_t *p = cells; 164 165 *p++ = va; 166 *p++ = size; 167 168 if (OF_call_method("release", ofw_mmu_ihandle, 169 2, 0, (int *)cells) == -1) { 170 return -1; 171 } 172 173 return 0; 174 } 175 176 static int 177 ofw_map(vaddr_t va, paddr_t pa, vsize_t size) 178 { 179 /* (2) phys, (1) virt, (1) size, (1) mode -> nil */ 180 uint32_t cells[2+1+1+1]; 181 uint32_t *p = cells; 182 183 if (ofw_address_cells == 2) { 184 *p++ = ((uint64_t)pa) >> 32; 185 *p++ = (uint32_t)pa; 186 } else { 187 *p++ = (uint32_t)pa; 188 } 189 190 *p++ = va; 191 *p++ = size; 192 *p++ = 0x00000010 /* PTE_SO | PTE_M */; /* XXX magic numbers */ 193 194 if (OF_call_method("map", ofw_mmu_ihandle, 195 ofw_address_cells + 3, 0, (int *)cells) == -1) { 196 return -1; 197 } 198 199 return 0; 200 } 201 202 static int 203 ofw_create_mapping(vaddr_t va, paddr_t pa, vsize_t size) 204 { 205 if (ofw_claimphys(pa, size) == -1) { 206 printf("!!! FAILED TO CLAIM PHYS 0x%lx size 0x%lx\n", 207 pa, size); 208 return -1; 209 } 210 211 /* 212 * If we're running in real-mode, the claimphys is enough. 213 */ 214 if (ofw_real_mode) { 215 return 0; 216 } 217 218 if (ofw_claimvirt(va, size) == -1) { 219 printf("!!! FAILED TO CLAIM VIRT 0x%lx size 0x%lx\n", 220 va, size); 221 ofw_releasephys(pa, size); 222 return -1; 223 } 224 if (ofw_map(va, pa, size) == -1) { 225 printf("!!! FAILED TO MAP PHYS 0x%lx @ 0x%lx size 0x%lx\n", 226 pa, va, size); 227 ofw_releasevirt(va, size); 228 ofw_releasephys(pa, size); 229 return -1; 230 } 231 232 return 0; 233 } 234 235 /* 236 * This structure describes a physically contiguous mapping 237 * for the loaded kernel. We assume VA==PA, which would be 238 * equivalent to running in real-mode. 239 */ 240 #define MAX_KMAPPINGS 64 241 struct kmapping { 242 vaddr_t vstart; 243 vaddr_t vend; 244 } kmappings[MAX_KMAPPINGS]; 245 246 #define TRUNC_PAGE(x) ((x) & ~((unsigned long)NBPG - 1)) 247 #define ROUND_PAGE(x) TRUNC_PAGE((x) + (NBPG - 1)) 248 249 /* 250 * Enter a mapping for the loaded kernel. If the start is within an 251 * already mapped region, or if it starts immediately following a 252 * previous region, we extend it. 253 */ 254 static int 255 kmapping_enter(vaddr_t va, vsize_t size) 256 { 257 struct kmapping *km, *km_prev, *km_next, *km_last; 258 vaddr_t va_end; 259 int i; 260 261 km_last = &kmappings[MAX_KMAPPINGS - 1]; 262 263 /* Round the region to a page. */ 264 va_end = ROUND_PAGE(va + size); 265 266 /* Truncate the region to the nearest page boundary. */ 267 va = TRUNC_PAGE(va); 268 269 /* Get the rounded size. */ 270 size = va_end - va; 271 272 DPRINTF("kmapping_enter: va=0x%lx size=0x%lx\n", 273 va, size); 274 275 /* Check to see if there's an overlapping entry in the table. */ 276 for (i = 0, km_prev = NULL; i < MAX_KMAPPINGS; i++, km_prev = km) { 277 km = &kmappings[i]; 278 km_next = (km == km_last) ? NULL : (km + 1); 279 280 if (km->vstart == km->vend) { 281 /* 282 * Found an empty slot. We are guaranteed there 283 * is not slot after this to merge with. 284 */ 285 DPRINTF("!!! entering into empty slot (%d)\n", i); 286 goto enter_new; 287 } 288 289 if (va >= km->vstart) { 290 if (va_end <= km->vend) { 291 /* This region is already fully mapped. */ 292 DPRINTF("!!! matches existing region " 293 "va=0x%lx-0x%lx\n", 294 km->vstart, km->vend); 295 return 0; 296 } 297 if (va > km->vend) { 298 /* Requested region falls after this one. */ 299 continue; 300 } 301 302 /* 303 * We will extend this region. Adjust the 304 * start and size. 305 */ 306 va = km->vend; 307 size = va_end - va; 308 309 /* 310 * If there is a slot after this one and it's 311 * not empty, see if these two can be merged. 312 */ 313 if (km_next != NULL && 314 km_next->vstart != km_next->vend && 315 va_end >= km_next->vstart) { 316 if (va_end > km_next->vend) { 317 /* Crazy! */ 318 printf("!!! GOBBLED UP KM_NEXT!\n"); 319 return 1; 320 } 321 va_end = km_next->vstart; 322 size = va_end - va; 323 324 DPRINTF("!!! merging va=0x%lx-0x%lx and " 325 "va=0x%lx-0x%lx\n", 326 km->vstart, km->vend, 327 km_next->vstart, km_next->vend); 328 goto merge_two; 329 } 330 331 DPRINTF("!!! extending existing region " 332 "va=0x%lx-0x%lx to " 333 "va=0x%lx-0x%lx\n", 334 km->vstart, km->vend, 335 km->vstart, va_end); 336 goto extend_existing; 337 } 338 339 /* 340 * We know that the new region starts before the current 341 * one. Check to see of the end overlaps. 342 */ 343 if (va_end >= km->vstart) { 344 va_end = km->vstart; 345 size = va_end - va; 346 347 /* 348 * No need to check for a merge here; we would 349 * have caught it above. 350 */ 351 goto prepend_existing; 352 } 353 354 /* 355 * Need to the new region in front of the current one. 356 * Make sure there's room. 357 */ 358 if (km_next == NULL || km_last->vstart != km_last->vend) { 359 printf("!!! NO ROOM TO INSERT MAPPING\n"); 360 return 1; 361 } 362 363 if (km_prev == NULL) { 364 DPRINTF("!!! entering before 0x%lx-0x%lx\n", 365 km->vstart, km->vend); 366 } else { 367 DPRINTF("!!! entering between 0x%lx-0x%lx and " 368 "0x%lx-0x%lx\n", 369 km_prev->vstart, km_prev->vend, 370 km->vstart, km->vend); 371 } 372 373 if (ofw_create_mapping(va, va, size) == -1) { 374 return 1; 375 } 376 size = (uintptr_t)(&kmappings[MAX_KMAPPINGS]) - 377 (uintptr_t)(km_next + 1); 378 memmove(km_next, km, size); 379 km->vstart = va; 380 km->vend = va_end; 381 return 0; 382 } 383 384 extend_existing: 385 if (ofw_create_mapping(va, va, size) == -1) { 386 return 1; 387 } 388 km->vend = va_end; 389 return 0; 390 391 prepend_existing: 392 if (ofw_create_mapping(va, va, size) == -1) { 393 return 1; 394 } 395 km->vstart = va; 396 return 0; 397 398 enter_new: 399 if (ofw_create_mapping(va, va, size) == -1) { 400 return 1; 401 } 402 km->vstart = va; 403 km->vend = va_end; 404 return 0; 405 406 merge_two: 407 if (ofw_create_mapping(va, va, size) == -1) { 408 return 1; 409 } 410 km->vend = km_next->vend; 411 size = 412 (uintptr_t)(&kmappings[MAX_KMAPPINGS]) - (uintptr_t)(km_next + 1); 413 if (size != 0) { 414 memmove(km_next, km_next + 1, size); 415 } 416 km_last->vstart = km_last->vend = 0; 417 return 0; 418 } 419 420 /* 421 * loadfile() hooks. 422 */ 423 ssize_t 424 macppc_read(int f, void *addr, size_t size) 425 { 426 if (kmapping_enter((vaddr_t)addr, size)) { 427 return -1; 428 } 429 return read(f, addr, size); 430 } 431 432 void * 433 macppc_memcpy(void *dst, const void *src, size_t size) 434 { 435 if (kmapping_enter((vaddr_t)dst, size)) { 436 panic("macppc_memcpy: kmapping_enter failed"); 437 } 438 return memcpy(dst, src, size); 439 } 440 441 void * 442 macppc_memset(void *dst, int c, size_t size) 443 { 444 if (kmapping_enter((vaddr_t)dst, size)) { 445 panic("macppc_memset: kmapping_enter failed"); 446 } 447 return memset(dst, c, size); 448 } 449