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