1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 #include <sys/types.h>
29 #include <sys/saio.h>
30 #include <sys/sysmacros.h>
31 #include <sys/promif.h>
32 #include <sys/bootconf.h>
33 #include <sys/salib.h>
34
35 #define NIL 0
36
37 #ifdef DEBUG
38 static int resalloc_debug = 1;
39 #else /* DEBUG */
40 static int resalloc_debug = 0;
41 #endif /* DEBUG */
42 #define dprintf if (resalloc_debug) printf
43
44 extern struct memlist *vfreelistp, *pfreelistp;
45 extern void reset_alloc(void);
46 extern void alloc_segment(caddr_t);
47
48 caddr_t memlistpage;
49 caddr_t le_page;
50 caddr_t ie_page;
51 caddr_t scratchmemp;
52 extern int pagesize;
53
54 #define N_FREELIST 20 /* keep the largest 20 free regions */
55 static size_t free_size[N_FREELIST];
56 static caddr_t free_addr[N_FREELIST];
57
58 /*
59 * OBP sets up a 1:1 mapping of virtual to physical in the range 8KB-10MB. The
60 * standalone is free to use any or all of this during its lifetime.
61 * Unfortunately, some platforms (Serengeti and LW8) can't use the full range.
62 * See 4799331 for more details. Limited platforms can use up to
63 * MAPPEDMEM_MINTOP; everyone else can use up to MAPPEDMEM_FULLTOP.
64 * resalloc_init makes the determination as to how much the machine being booted
65 * can use.
66 *
67 * But wait! There's more! resalloc handles three types of allocations: Two
68 * flavors of RES_BOOTSCRATCH (RES_BOOTSCRATCH and RES_BOOTSCRATCH_NOFAIL), and
69 * one of RES_CHILDVIRT. RES_CHILDVIRT is handled by prom_alloc, and is boring.
70 * We handle RES_BOOTSCRATCH allocations ourselves using the portion of the 1:1
71 * range not consumed by boot. The unconsumed range is subdivided into two
72 * portions - the general area from top_resvmem to top_bootmem and the reserved
73 * area from above memlistpage to top_resvmem. Both RES_BOOTSCRATCH flavors are
74 * satisfied by the general area until said area is exhausted, at which point
75 * RES_BOOTSCRATCH allocations return failure. RES_BOOTSCRATCH_NOFAIL
76 * allocations can't fail, so we'll try to satisfy them from the reserved area
77 * if the general area is full. If we still can't satisfy the nofail
78 * allocation, we'll call prom_panic.
79 *
80 * This whole boot memory allocation thing needs some serious rethinking.
81 *
82 * Memory layout:
83 *
84 * |-------| top_bootmem
85 * | | } MAPPEDMEM_FULLTOP (only on non-serengeti, lw8)
86 * | | } MAPPEDMEM_MINTOP
87 * |-------| top_resvmem/scratchmemp
88 * | | } MAPPEDMEM_RESERVE
89 * |-------| scratchresvp
90 * | | } one page
91 * |-------| memlistpage (at roundup(_end, pagesize))
92 * |-------| _end
93 * | boot |
94 * : :
95 *
96 */
97
98 #define MAPPEDMEM_RESERVE (512*1024) /* reserved for NOFAIL allocs */
99
100 #define MAPPEDMEM_MINTOP (caddr_t)(6*1024*1024)
101 #define MAPPEDMEM_FULLTOP (caddr_t)(10*1024*1024)
102
103 static caddr_t top_bootmem = MAPPEDMEM_MINTOP;
104 static caddr_t top_resvmem, scratchresvp;
105
106 /*
107 * with newboot, boot goes away when it launches the client,
108 * so we can safely extend bootmem on sg, and give it back
109 * before we die.
110 */
111 int is_sg;
112 caddr_t sg_addr;
113 size_t sg_len;
114
115 static int
impl_name(char * buf,size_t bufsz)116 impl_name(char *buf, size_t bufsz)
117 {
118 pnode_t n = prom_rootnode();
119 size_t len = prom_getproplen(n, "name");
120
121 if (len == 0 || len >= bufsz)
122 return (-1);
123
124 (void) prom_getprop(n, "name", buf);
125 buf[len] = '\0';
126
127 return (0);
128 }
129
130 static caddr_t
vpage_from_freelist(size_t bytes)131 vpage_from_freelist(size_t bytes)
132 {
133 caddr_t v;
134 int i;
135
136 /* find first region which fits */
137 for (i = 0; i < N_FREELIST && free_size[i] < bytes; i++)
138 continue;
139
140 if (i == N_FREELIST) {
141 dprintf("boot: failed to allocate %lu bytes from scratch "
142 "memory\n", bytes);
143 return (NULL);
144 }
145
146 v = free_addr[i];
147 free_addr[i] += bytes;
148 free_size[i] -= bytes;
149 dprintf("reuse freed temp scratch: bytes = %lu at %p\n", bytes,
150 (void *)v);
151 return (v);
152 }
153
154 /*
155 * This routine will find the next PAGESIZE chunk in the
156 * low MAPPEDMEM_MINTOP. It is analogous to valloc(). It is only for boot
157 * scratch memory, because child scratch memory goes up in
158 * the the high memory. We just need to verify that the
159 * pages are on the list. The calling routine will actually
160 * remove them.
161 */
162 static caddr_t
get_low_vpage(size_t numpages,enum RESOURCES type)163 get_low_vpage(size_t numpages, enum RESOURCES type)
164 {
165 size_t bytes;
166 caddr_t v;
167
168 if (!numpages)
169 return (0);
170
171 /* We know the page is mapped because the 1st MAPPEDMEM_MINTOP is 1:1 */
172 bytes = numpages * pagesize;
173 if (scratchmemp + bytes <= top_bootmem) {
174 v = scratchmemp;
175 scratchmemp += bytes;
176 return (v);
177 }
178
179 /*
180 * If we run out of scratch memory, look in the freelist
181 */
182 if ((v = vpage_from_freelist(bytes)) != NULL)
183 return (v);
184
185 /*
186 * Try really hard for allocations that can't fail. Look in the area
187 * that we've reserved for them.
188 */
189 if (type == RES_BOOTSCRATCH_NOFAIL) {
190 if (scratchresvp + bytes <= top_resvmem) {
191 v = scratchresvp;
192 scratchresvp += bytes;
193 dprintf("using %lu bytes of reserved mem (%lu left)\n",
194 bytes, top_resvmem - scratchresvp);
195 return (v);
196 } else {
197 printf("boot: failed to allocate %lu bytes from "
198 "reserved scratch memory\n", bytes);
199 prom_panic("boot: scratch memory overflow.\n");
200 }
201 }
202
203 return (NULL);
204 }
205
206 void
resalloc_init(void)207 resalloc_init(void)
208 {
209 char iarch[128];
210
211 if (impl_name(iarch, sizeof (iarch)) < 0) {
212 dprintf("boot: resalloc_init: failed to read iarch\n");
213 return;
214 }
215
216 dprintf("boot: resalloc_init: got iarch %s\n", iarch);
217
218 /*
219 * Some versions of SG/LW8 firmware can actually handle the entire 10MB,
220 * but we don't have the ability to check for the firmware version here.
221 */
222 if (strcmp(iarch, "SUNW,Sun-Fire") == 0 ||
223 strcmp(iarch, "SUNW,Netra-T12") == 0) {
224 is_sg = 1;
225 sg_addr = MAPPEDMEM_MINTOP;
226 sg_len = MAPPEDMEM_FULLTOP - MAPPEDMEM_MINTOP;
227 if (prom_alloc(sg_addr, sg_len, 1) != sg_addr)
228 prom_panic("can't extend sg bootmem");
229 }
230
231 top_bootmem = MAPPEDMEM_FULLTOP;
232
233 dprintf("boot: resalloc_init: boosted top_bootmem to %p\n",
234 (void *)top_bootmem);
235 }
236
237 caddr_t
resalloc(enum RESOURCES type,size_t bytes,caddr_t virthint,int align)238 resalloc(enum RESOURCES type, size_t bytes, caddr_t virthint, int align)
239 {
240 caddr_t vaddr;
241 long pmap = 0;
242
243 if (memlistpage == (caddr_t)0)
244 reset_alloc();
245
246 if (bytes == 0)
247 return ((caddr_t)0);
248
249 /* extend request to fill a page */
250 bytes = roundup(bytes, pagesize);
251
252 dprintf("resalloc: bytes = %lu\n", bytes);
253
254 switch (type) {
255
256 /*
257 * even V2 PROMs never bother to indicate whether the
258 * first MAPPEDMEM_MINTOP is taken or not. So we do it all here.
259 * Smart PROM or no smart PROM.
260 */
261 case RES_BOOTSCRATCH:
262 case RES_BOOTSCRATCH_NOFAIL:
263 vaddr = get_low_vpage((bytes/pagesize), type);
264
265 if (resalloc_debug) {
266 dprintf("vaddr = %p, paddr = %lx\n", (void *)vaddr,
267 ptob(pmap));
268 print_memlist(vfreelistp);
269 print_memlist(pfreelistp);
270 }
271 return (vaddr);
272 /*NOTREACHED*/
273
274 case RES_CHILDVIRT:
275 vaddr = (caddr_t)prom_alloc(virthint, bytes, align);
276
277 if (vaddr == (caddr_t)virthint)
278 return (vaddr);
279 printf("Alloc of 0x%lx bytes at 0x%p refused.\n",
280 bytes, (void *)virthint);
281 return ((caddr_t)0);
282 /*NOTREACHED*/
283
284 default:
285 printf("Bad resurce type\n");
286 return ((caddr_t)0);
287 }
288 }
289
290 #ifdef lint
291 static char _end[1]; /* defined by the linker! */
292 #endif /* lint */
293
294 void
reset_alloc(void)295 reset_alloc(void)
296 {
297 extern char _end[];
298
299 /* Cannot be called multiple times */
300 if (memlistpage != (caddr_t)0)
301 return;
302
303 /*
304 * Due to kernel history and ease of programming, we
305 * want to keep everything private to /boot BELOW MAPPEDMEM_MINTOP.
306 * In this way, the kernel can just snarf it all when
307 * when it is ready, and not worry about snarfing lists.
308 */
309 memlistpage = (caddr_t)roundup((uintptr_t)_end, pagesize);
310
311 /*
312 * This next is for scratch memory only
313 * We only need 1 page in memlistpage for now
314 */
315 scratchresvp = (caddr_t)(memlistpage + pagesize);
316 scratchmemp = top_resvmem = scratchresvp + MAPPEDMEM_RESERVE;
317 le_page = (caddr_t)(scratchmemp + pagesize);
318 ie_page = (caddr_t)(le_page + pagesize);
319
320 bzero(memlistpage, pagesize);
321 bzero(scratchmemp, pagesize);
322 dprintf("memlistpage = %p\n", (void *)memlistpage);
323 dprintf("le_page = %p\n", (void *)le_page);
324 }
325
326 void
resfree(enum RESOURCES type,caddr_t virtaddr,size_t size)327 resfree(enum RESOURCES type, caddr_t virtaddr, size_t size)
328 {
329 int i;
330
331 /* make sure this is boot scratch memory */
332 switch (type) {
333 case RES_BOOTSCRATCH:
334 if (virtaddr + size > top_bootmem)
335 return;
336 break;
337 default:
338 return;
339 }
340
341 /*
342 * Add this to the end of the free list
343 * NOTE: This relies on the fact that KRTLD calls BOP_FREE
344 * from largest to smallest chunks.
345 */
346 for (i = 0; i < N_FREELIST && free_size[i]; i++)
347 ;
348 if (i == N_FREELIST)
349 return;
350 free_size[i] = size;
351 free_addr[i] = virtaddr;
352 }
353