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