xref: /illumos-gate/usr/src/psm/stand/boot/sparc/common/sun4x_standalloc.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
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
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
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
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
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
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
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
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