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
38static int	resalloc_debug = 1;
39#else /* DEBUG */
40static int	resalloc_debug = 0;
41#endif /* DEBUG */
42#define	dprintf	if (resalloc_debug) printf
43
44extern struct memlist	*vfreelistp, *pfreelistp;
45extern	void		reset_alloc(void);
46extern	void		alloc_segment(caddr_t);
47
48caddr_t		memlistpage;
49caddr_t		le_page;
50caddr_t		ie_page;
51caddr_t 	scratchmemp;
52extern int	pagesize;
53
54#define	N_FREELIST	20	/* keep the largest 20 free regions */
55static size_t	free_size[N_FREELIST];
56static 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
103static caddr_t top_bootmem = MAPPEDMEM_MINTOP;
104static 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 */
111int is_sg;
112caddr_t sg_addr;
113size_t sg_len;
114
115static int
116impl_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
130static caddr_t
131vpage_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 */
162static caddr_t
163get_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
206void
207resalloc_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
237caddr_t
238resalloc(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
291static char _end[1];	/* defined by the linker! */
292#endif	/* lint */
293
294void
295reset_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
326void
327resfree(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