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