loadfile_machdep.c revision 1.1.4.2 1 /* $NetBSD: loadfile_machdep.c,v 1.1.4.2 2021/04/03 22:28:30 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 /* order of cells is phys.lo ... phys.hi */
60 *p++ = (uint32_t)pa;
61 *p++ = ((uint64_t)pa) >> 32;
62 } else {
63 *p++ = (uint32_t)pa;
64 }
65
66 #if 0 /* No known Mac systems with 2, and spec is ambiguous about order. */
67 if (ofw_size_cells == 2) {
68 *p++ = ((uint64_t)size) >> 32;
69 *p++ = (uint32_t)size;
70 } else
71 #endif
72 *p++ = (uint32_t)size;
73
74 *p++ = 0; /* align */
75
76 if (OF_call_method("claim", ofw_memory_ihandle,
77 ofw_address_cells + ofw_size_cells + 1,
78 ofw_address_cells, (int *)cells) == -1) {
79 return -1;
80 }
81
82 if (ofw_address_cells == 2) {
83 /* order of cells is base.lo ... base.hi */
84 uint64_t v;
85 v = *p++;
86 v |= (uint64_t)(*p++) << 32;
87 result = (paddr_t)v;
88 } else {
89 result = *p++;
90 }
91
92 if (result != pa) {
93 printf("!!! CLAIM PHYS 0x%lx RETURNED 0x%lx\n",
94 pa, result);
95 return -1;
96 }
97
98 return 0;
99 }
100
101 static int
102 ofw_releasephys(paddr_t pa, vsize_t size)
103 {
104 /* (2) phys, (2) size, -> nil */
105 uint32_t cells[2+2];
106 uint32_t *p = cells;
107
108 if (ofw_address_cells == 2) {
109 /* order of cells is phys.lo ... phys.hi */
110 *p++ = (uint32_t)pa;
111 *p++ = ((uint64_t)pa) >> 32;
112 } else {
113 *p++ = (uint32_t)pa;
114 }
115
116 #if 0 /* No known Mac systems with 2, and spec is ambiguous about order. */
117 if (ofw_size_cells == 2) {
118 *p++ = ((uint64_t)size) >> 32;
119 *p++ = (uint32_t)size;
120 } else
121 #endif
122 *p++ = (uint32_t)size;
123
124 if (OF_call_method("release", ofw_memory_ihandle,
125 ofw_address_cells + ofw_size_cells,
126 0, (int *)cells) == -1) {
127 return -1;
128 }
129
130 return 0;
131 }
132
133 static int
134 ofw_claimvirt(vaddr_t va, vsize_t size)
135 {
136 /* (1) virt, (1) size, (1) align -> (1) virt */
137 uint32_t cells[1+1+1+1];
138 uint32_t *p = cells;
139 vaddr_t result;
140
141 *p++ = va;
142 *p++ = size;
143 *p++ = 0; /* align */
144
145 if (OF_call_method("claim", ofw_mmu_ihandle,
146 3, 1, (int *)cells) == -1) {
147 return -1;
148 }
149
150 result = *p++;
151
152 if (result != va) {
153 printf("!!! CLAIM VIRT 0x%lx RETURNED 0x%lx\n",
154 va, result);
155 return -1;
156 }
157
158 return 0;
159 }
160
161 static int
162 ofw_releasevirt(vaddr_t va, vsize_t size)
163 {
164 /* (1) virt, (1) size -> nil */
165 uint32_t cells[1+1];
166 uint32_t *p = cells;
167
168 *p++ = va;
169 *p++ = size;
170
171 if (OF_call_method("release", ofw_mmu_ihandle,
172 2, 0, (int *)cells) == -1) {
173 return -1;
174 }
175
176 return 0;
177 }
178
179 static int
180 ofw_map(vaddr_t va, paddr_t pa, vsize_t size)
181 {
182 /* (2) phys, (1) virt, (1) size, (1) mode -> nil */
183 uint32_t cells[2+1+1+1];
184 uint32_t *p = cells;
185
186 if (ofw_address_cells == 2) {
187 /* order of cells is phys.lo ... phys.hi */
188 *p++ = (uint32_t)pa;
189 *p++ = ((uint64_t)pa) >> 32;
190 } else {
191 *p++ = (uint32_t)pa;
192 }
193
194 *p++ = va;
195 *p++ = size;
196 *p++ = 0x00000010 /* PTE_SO | PTE_M */; /* XXX magic numbers */
197
198 if (OF_call_method("map", ofw_mmu_ihandle,
199 ofw_address_cells + 3, 0, (int *)cells) == -1) {
200 return -1;
201 }
202
203 return 0;
204 }
205
206 static int
207 ofw_create_mapping(vaddr_t va, paddr_t pa, vsize_t size)
208 {
209 if (ofw_claimphys(pa, size) == -1) {
210 printf("!!! FAILED TO CLAIM PHYS 0x%lx size 0x%lx\n",
211 pa, size);
212 return -1;
213 }
214
215 /*
216 * If we're running in real-mode, the claimphys is enough.
217 */
218 if (ofw_real_mode) {
219 return 0;
220 }
221
222 if (ofw_claimvirt(va, size) == -1) {
223 printf("!!! FAILED TO CLAIM VIRT 0x%lx size 0x%lx\n",
224 va, size);
225 ofw_releasephys(pa, size);
226 return -1;
227 }
228 if (ofw_map(va, pa, size) == -1) {
229 printf("!!! FAILED TO MAP PHYS 0x%lx @ 0x%lx size 0x%lx\n",
230 pa, va, size);
231 ofw_releasevirt(va, size);
232 ofw_releasephys(pa, size);
233 return -1;
234 }
235
236 return 0;
237 }
238
239 /*
240 * This structure describes a physically contiguous mapping
241 * for the loaded kernel. We assume VA==PA, which would be
242 * equivalent to running in real-mode.
243 */
244 #define MAX_KMAPPINGS 64
245 struct kmapping {
246 vaddr_t vstart;
247 vaddr_t vend;
248 } kmappings[MAX_KMAPPINGS];
249
250 #define TRUNC_PAGE(x) ((x) & ~((unsigned long)NBPG - 1))
251 #define ROUND_PAGE(x) TRUNC_PAGE((x) + (NBPG - 1))
252
253 /*
254 * Enter a mapping for the loaded kernel. If the start is within an
255 * already mapped region, or if it starts immediately following a
256 * previous region, we extend it.
257 */
258 static int
259 kmapping_enter(vaddr_t va, vsize_t size)
260 {
261 struct kmapping *km, *km_prev, *km_next, *km_last;
262 vaddr_t va_end;
263 int i;
264
265 km_last = &kmappings[MAX_KMAPPINGS - 1];
266
267 /* Round the region to a page. */
268 va_end = ROUND_PAGE(va + size);
269
270 /* Truncate the region to the nearest page boundary. */
271 va = TRUNC_PAGE(va);
272
273 /* Get the rounded size. */
274 size = va_end - va;
275
276 DPRINTF("kmapping_enter: va=0x%lx size=0x%lx\n",
277 va, size);
278
279 /* Check to see if there's an overlapping entry in the table. */
280 for (i = 0, km_prev = NULL; i < MAX_KMAPPINGS; i++, km_prev = km) {
281 km = &kmappings[i];
282 km_next = (km == km_last) ? NULL : (km + 1);
283
284 if (km->vstart == km->vend) {
285 /*
286 * Found an empty slot. We are guaranteed there
287 * is not slot after this to merge with.
288 */
289 DPRINTF("!!! entering into empty slot (%d)\n", i);
290 goto enter_new;
291 }
292
293 if (va >= km->vstart) {
294 if (va_end <= km->vend) {
295 /* This region is already fully mapped. */
296 DPRINTF("!!! matches existing region "
297 "va=0x%lx-0x%lx\n",
298 km->vstart, km->vend);
299 return 0;
300 }
301 if (va > km->vend) {
302 /* Requested region falls after this one. */
303 continue;
304 }
305
306 /*
307 * We will extend this region. Adjust the
308 * start and size.
309 */
310 va = km->vend;
311 size = va_end - va;
312
313 /*
314 * If there is a slot after this one and it's
315 * not empty, see if these two can be merged.
316 */
317 if (km_next != NULL &&
318 km_next->vstart != km_next->vend &&
319 va_end >= km_next->vstart) {
320 if (va_end > km_next->vend) {
321 /* Crazy! */
322 printf("!!! GOBBLED UP KM_NEXT!\n");
323 return 1;
324 }
325 va_end = km_next->vstart;
326 size = va_end - va;
327
328 DPRINTF("!!! merging va=0x%lx-0x%lx and "
329 "va=0x%lx-0x%lx\n",
330 km->vstart, km->vend,
331 km_next->vstart, km_next->vend);
332 goto merge_two;
333 }
334
335 DPRINTF("!!! extending existing region "
336 "va=0x%lx-0x%lx to "
337 "va=0x%lx-0x%lx\n",
338 km->vstart, km->vend,
339 km->vstart, va_end);
340 goto extend_existing;
341 }
342
343 /*
344 * We know that the new region starts before the current
345 * one. Check to see of the end overlaps.
346 */
347 if (va_end >= km->vstart) {
348 va_end = km->vstart;
349 size = va_end - va;
350
351 /*
352 * No need to check for a merge here; we would
353 * have caught it above.
354 */
355 goto prepend_existing;
356 }
357
358 /*
359 * Need to the new region in front of the current one.
360 * Make sure there's room.
361 */
362 if (km_next == NULL || km_last->vstart != km_last->vend) {
363 printf("!!! NO ROOM TO INSERT MAPPING\n");
364 return 1;
365 }
366
367 if (km_prev == NULL) {
368 DPRINTF("!!! entering before 0x%lx-0x%lx\n",
369 km->vstart, km->vend);
370 } else {
371 DPRINTF("!!! entering between 0x%lx-0x%lx and "
372 "0x%lx-0x%lx\n",
373 km_prev->vstart, km_prev->vend,
374 km->vstart, km->vend);
375 }
376
377 if (ofw_create_mapping(va, va, size) == -1) {
378 return 1;
379 }
380 size = (uintptr_t)(&kmappings[MAX_KMAPPINGS]) -
381 (uintptr_t)(km_next + 1);
382 memmove(km_next, km, size);
383 km->vstart = va;
384 km->vend = va_end;
385 return 0;
386 }
387
388 extend_existing:
389 if (ofw_create_mapping(va, va, size) == -1) {
390 return 1;
391 }
392 km->vend = va_end;
393 return 0;
394
395 prepend_existing:
396 if (ofw_create_mapping(va, va, size) == -1) {
397 return 1;
398 }
399 km->vstart = va;
400 return 0;
401
402 enter_new:
403 if (ofw_create_mapping(va, va, size) == -1) {
404 return 1;
405 }
406 km->vstart = va;
407 km->vend = va_end;
408 return 0;
409
410 merge_two:
411 if (ofw_create_mapping(va, va, size) == -1) {
412 return 1;
413 }
414 km->vend = km_next->vend;
415 size =
416 (uintptr_t)(&kmappings[MAX_KMAPPINGS]) - (uintptr_t)(km_next + 1);
417 if (size != 0) {
418 memmove(km_next, km_next + 1, size);
419 }
420 km_last->vstart = km_last->vend = 0;
421 return 0;
422 }
423
424 /*
425 * loadfile() hooks.
426 */
427 ssize_t
428 macppc_read(int f, void *addr, size_t size)
429 {
430 if (kmapping_enter((vaddr_t)addr, size)) {
431 return -1;
432 }
433 return read(f, addr, size);
434 }
435
436 void *
437 macppc_memcpy(void *dst, const void *src, size_t size)
438 {
439 if (kmapping_enter((vaddr_t)dst, size)) {
440 panic("macppc_memcpy: kmapping_enter failed");
441 }
442 return memcpy(dst, src, size);
443 }
444
445 void *
446 macppc_memset(void *dst, int c, size_t size)
447 {
448 if (kmapping_enter((vaddr_t)dst, size)) {
449 panic("macppc_memset: kmapping_enter failed");
450 }
451 return memset(dst, c, size);
452 }
453