xref: /illumos-gate/usr/src/uts/sun4/os/memlist.c (revision 56f33205)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5843e1988Sjohnlev  * Common Development and Distribution License (the "License").
6843e1988Sjohnlev  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*56f33205SJonathan Adams  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <sys/types.h>
277c478bd9Sstevel@tonic-gate #include <sys/param.h>
287c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
297c478bd9Sstevel@tonic-gate #include <sys/signal.h>
307c478bd9Sstevel@tonic-gate #include <sys/systm.h>
317c478bd9Sstevel@tonic-gate #include <sys/user.h>
327c478bd9Sstevel@tonic-gate #include <sys/mman.h>
337c478bd9Sstevel@tonic-gate #include <sys/class.h>
347c478bd9Sstevel@tonic-gate #include <sys/proc.h>
357c478bd9Sstevel@tonic-gate #include <sys/procfs.h>
367c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
377c478bd9Sstevel@tonic-gate #include <sys/cred.h>
387c478bd9Sstevel@tonic-gate #include <sys/archsystm.h>
397c478bd9Sstevel@tonic-gate #include <sys/machsystm.h>
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate #include <sys/reboot.h>
427c478bd9Sstevel@tonic-gate #include <sys/uadmin.h>
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
457c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
467c478bd9Sstevel@tonic-gate #include <sys/session.h>
477c478bd9Sstevel@tonic-gate #include <sys/ucontext.h>
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate #include <sys/dnlc.h>
507c478bd9Sstevel@tonic-gate #include <sys/var.h>
517c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
527c478bd9Sstevel@tonic-gate #include <sys/debug.h>
537c478bd9Sstevel@tonic-gate #include <sys/thread.h>
547c478bd9Sstevel@tonic-gate #include <sys/vtrace.h>
557c478bd9Sstevel@tonic-gate #include <sys/consdev.h>
567c478bd9Sstevel@tonic-gate #include <sys/frame.h>
577c478bd9Sstevel@tonic-gate #include <sys/stack.h>
587c478bd9Sstevel@tonic-gate #include <sys/swap.h>
597c478bd9Sstevel@tonic-gate #include <sys/vmparam.h>
607c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate #include <sys/privregs.h>
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate #include <vm/hat.h>
657c478bd9Sstevel@tonic-gate #include <vm/anon.h>
667c478bd9Sstevel@tonic-gate #include <vm/as.h>
677c478bd9Sstevel@tonic-gate #include <vm/page.h>
687c478bd9Sstevel@tonic-gate #include <vm/seg.h>
697c478bd9Sstevel@tonic-gate #include <vm/seg_kmem.h>
707c478bd9Sstevel@tonic-gate #include <vm/seg_map.h>
717c478bd9Sstevel@tonic-gate #include <vm/seg_vn.h>
7200e145c7Skchow #include <vm/vm_dep.h>
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate #include <sys/exec.h>
757c478bd9Sstevel@tonic-gate #include <sys/acct.h>
767c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
777c478bd9Sstevel@tonic-gate #include <sys/tuneable.h>
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate #include <c2/audit.h>
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate #include <sys/trap.h>
827c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
837c478bd9Sstevel@tonic-gate #include <sys/bootconf.h>
847c478bd9Sstevel@tonic-gate #include <sys/memlist.h>
857c478bd9Sstevel@tonic-gate #include <sys/memlist_plat.h>
867c478bd9Sstevel@tonic-gate #include <sys/systeminfo.h>
877c478bd9Sstevel@tonic-gate #include <sys/promif.h>
88986fd29aSsetje #include <sys/prom_plat.h>
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate u_longlong_t	spec_hole_start = 0x80000000000ull;
917c478bd9Sstevel@tonic-gate u_longlong_t	spec_hole_end = 0xfffff80000000000ull;
927c478bd9Sstevel@tonic-gate 
93843e1988Sjohnlev pgcnt_t
num_phys_pages()94843e1988Sjohnlev num_phys_pages()
95843e1988Sjohnlev {
96843e1988Sjohnlev 	pgcnt_t npages = 0;
97843e1988Sjohnlev 	struct memlist *mp;
98843e1988Sjohnlev 
99*56f33205SJonathan Adams 	for (mp = phys_install; mp != NULL; mp = mp->ml_next)
100*56f33205SJonathan Adams 		npages += mp->ml_size >> PAGESHIFT;
101843e1988Sjohnlev 
102843e1988Sjohnlev 	return (npages);
103843e1988Sjohnlev }
104843e1988Sjohnlev 
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate pgcnt_t
size_virtalloc(prom_memlist_t * avail,size_t nelems)107986fd29aSsetje size_virtalloc(prom_memlist_t *avail, size_t nelems)
1087c478bd9Sstevel@tonic-gate {
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	u_longlong_t	start, end;
1117c478bd9Sstevel@tonic-gate 	pgcnt_t		allocpages = 0;
1127c478bd9Sstevel@tonic-gate 	uint_t		hole_allocated = 0;
1137c478bd9Sstevel@tonic-gate 	uint_t		i;
1147c478bd9Sstevel@tonic-gate 
115986fd29aSsetje 	for (i = 0; i < nelems - 1; i++) {
1167c478bd9Sstevel@tonic-gate 
117986fd29aSsetje 		start = avail[i].addr + avail[i].size;
118986fd29aSsetje 		end = avail[i + 1].addr;
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate 		/*
1217c478bd9Sstevel@tonic-gate 		 * Notes:
1227c478bd9Sstevel@tonic-gate 		 *
1237c478bd9Sstevel@tonic-gate 		 * (1) OBP on platforms with US I/II pre-allocates the hole
1247c478bd9Sstevel@tonic-gate 		 * represented by [spec_hole_start, spec_hole_end);
1257c478bd9Sstevel@tonic-gate 		 * pre-allocation is done to make this range unavailable
1267c478bd9Sstevel@tonic-gate 		 * for any allocation.
1277c478bd9Sstevel@tonic-gate 		 *
1287c478bd9Sstevel@tonic-gate 		 * (2) OBP on starcat always pre-allocates the hole similar to
1297c478bd9Sstevel@tonic-gate 		 * platforms with US I/II.
1307c478bd9Sstevel@tonic-gate 		 *
1317c478bd9Sstevel@tonic-gate 		 * (3) OBP on serengeti does _not_ pre-allocate the hole.
1327c478bd9Sstevel@tonic-gate 		 *
1337c478bd9Sstevel@tonic-gate 		 * (4) OBP ignores Spitfire Errata #21; i.e. it does _not_
1347c478bd9Sstevel@tonic-gate 		 * fill up or pre-allocate an additional 4GB on both sides
1357c478bd9Sstevel@tonic-gate 		 * of the hole.
1367c478bd9Sstevel@tonic-gate 		 *
1377c478bd9Sstevel@tonic-gate 		 * (5) kernel virtual range [spec_hole_start, spec_hole_end)
1387c478bd9Sstevel@tonic-gate 		 * is _not_ used on any platform including those with
1397c478bd9Sstevel@tonic-gate 		 * UltraSPARC III where there is no hole.
1407c478bd9Sstevel@tonic-gate 		 *
1417c478bd9Sstevel@tonic-gate 		 * Algorithm:
1427c478bd9Sstevel@tonic-gate 		 *
1437c478bd9Sstevel@tonic-gate 		 * Check if range [spec_hole_start, spec_hole_end) is
1447c478bd9Sstevel@tonic-gate 		 * pre-allocated by OBP; if so, subtract that range from
1457c478bd9Sstevel@tonic-gate 		 * allocpages.
1467c478bd9Sstevel@tonic-gate 		 */
1477c478bd9Sstevel@tonic-gate 		if (end >= spec_hole_end && start <= spec_hole_start)
1487c478bd9Sstevel@tonic-gate 			hole_allocated = 1;
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate 		allocpages += btopr(end - start);
1517c478bd9Sstevel@tonic-gate 	}
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	if (hole_allocated)
1547c478bd9Sstevel@tonic-gate 		allocpages -= btop(spec_hole_end - spec_hole_start);
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 	return (allocpages);
1577c478bd9Sstevel@tonic-gate }
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate /*
1607c478bd9Sstevel@tonic-gate  * Returns the max contiguous physical memory present in the
1617c478bd9Sstevel@tonic-gate  * memlist "physavail".
1627c478bd9Sstevel@tonic-gate  */
1637c478bd9Sstevel@tonic-gate uint64_t
get_max_phys_size(struct memlist * physavail)1647c478bd9Sstevel@tonic-gate get_max_phys_size(
1657c478bd9Sstevel@tonic-gate 	struct memlist	*physavail)
1667c478bd9Sstevel@tonic-gate {
1677c478bd9Sstevel@tonic-gate 	uint64_t	max_size = 0;
1687c478bd9Sstevel@tonic-gate 
169*56f33205SJonathan Adams 	for (; physavail; physavail = physavail->ml_next) {
170*56f33205SJonathan Adams 		if (physavail->ml_size > max_size)
171*56f33205SJonathan Adams 			max_size = physavail->ml_size;
1727c478bd9Sstevel@tonic-gate 	}
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	return (max_size);
1757c478bd9Sstevel@tonic-gate }
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 
178986fd29aSsetje static void
more_pages(uint64_t base,uint64_t len)179986fd29aSsetje more_pages(uint64_t base, uint64_t len)
180986fd29aSsetje {
181986fd29aSsetje 	void kphysm_add();
1827c478bd9Sstevel@tonic-gate 
183986fd29aSsetje 	kphysm_add(base, len, 1);
184986fd29aSsetje }
1857c478bd9Sstevel@tonic-gate 
186986fd29aSsetje static void
less_pages(uint64_t base,uint64_t len)187986fd29aSsetje less_pages(uint64_t base, uint64_t len)
1887c478bd9Sstevel@tonic-gate {
189986fd29aSsetje 	uint64_t pa, end = base + len;
1907c478bd9Sstevel@tonic-gate 	extern int kcage_on;
1917c478bd9Sstevel@tonic-gate 
192986fd29aSsetje 	for (pa = base; pa < end; pa += PAGESIZE) {
1937c478bd9Sstevel@tonic-gate 		pfn_t pfnum;
1947c478bd9Sstevel@tonic-gate 		page_t *pp;
1957c478bd9Sstevel@tonic-gate 
196986fd29aSsetje 		pfnum = (pfn_t)(pa >> PAGESHIFT);
197986fd29aSsetje 		if ((pp = page_numtopp_nolock(pfnum)) == NULL)
198986fd29aSsetje 			cmn_err(CE_PANIC, "missing pfnum %lx", pfnum);
199986fd29aSsetje 
200986fd29aSsetje 		/*
201986fd29aSsetje 		 * must break up any large pages that may have
202986fd29aSsetje 		 * constituent pages being utilized for
203986fd29aSsetje 		 * prom_alloc()'s. page_reclaim() can't handle
204986fd29aSsetje 		 * large pages.
205986fd29aSsetje 		 */
206986fd29aSsetje 		if (pp->p_szc != 0)
207986fd29aSsetje 			page_boot_demote(pp);
208986fd29aSsetje 
209986fd29aSsetje 		if (!PAGE_LOCKED(pp) && pp->p_lckcnt == 0) {
210986fd29aSsetje 			/*
211986fd29aSsetje 			 * Ahhh yes, a prom page,
212986fd29aSsetje 			 * suck it off the freelist,
213986fd29aSsetje 			 * lock it, and hashin on prom_pages vp.
214986fd29aSsetje 			 */
215986fd29aSsetje 			if (page_trylock(pp, SE_EXCL) == 0)
216986fd29aSsetje 				cmn_err(CE_PANIC, "prom page locked");
217986fd29aSsetje 
218986fd29aSsetje 			(void) page_reclaim(pp, NULL);
219986fd29aSsetje 			/*
220986fd29aSsetje 			 * vnode offsets on the prom_ppages vnode
221986fd29aSsetje 			 * are page numbers (gack) for >32 bit
222986fd29aSsetje 			 * physical memory machines.
223986fd29aSsetje 			 */
224af4c679fSSean McEnroe 			(void) page_hashin(pp, &promvp,
225986fd29aSsetje 			    (offset_t)pfnum, NULL);
226986fd29aSsetje 
227986fd29aSsetje 			if (kcage_on) {
228986fd29aSsetje 				ASSERT(pp->p_szc == 0);
22900e145c7Skchow 				if (PP_ISNORELOC(pp) == 0) {
23000e145c7Skchow 					PP_SETNORELOC(pp);
23100e145c7Skchow 					PLCNT_XFER_NORELOC(pp);
23200e145c7Skchow 				}
233986fd29aSsetje 			}
234986fd29aSsetje 			(void) page_pp_lock(pp, 0, 1);
235986fd29aSsetje 		}
236986fd29aSsetje 	}
237986fd29aSsetje }
238986fd29aSsetje 
239986fd29aSsetje void
diff_memlists(struct memlist * proto,struct memlist * diff,void (* func)())240986fd29aSsetje diff_memlists(struct memlist *proto, struct memlist *diff, void (*func)())
241986fd29aSsetje {
242986fd29aSsetje 	uint64_t p_base, p_end, d_base, d_end;
243986fd29aSsetje 
244986fd29aSsetje 	while (proto != NULL) {
245986fd29aSsetje 		/*
246986fd29aSsetje 		 * find diff item which may overlap with proto item
247986fd29aSsetje 		 * if none, apply func to all of proto item
248986fd29aSsetje 		 */
249986fd29aSsetje 		while (diff != NULL &&
250*56f33205SJonathan Adams 		    proto->ml_address >= diff->ml_address + diff->ml_size)
251*56f33205SJonathan Adams 			diff = diff->ml_next;
252986fd29aSsetje 		if (diff == NULL) {
253*56f33205SJonathan Adams 			(*func)(proto->ml_address, proto->ml_size);
254*56f33205SJonathan Adams 			proto = proto->ml_next;
255986fd29aSsetje 			continue;
256986fd29aSsetje 		}
257*56f33205SJonathan Adams 		if (proto->ml_address == diff->ml_address &&
258*56f33205SJonathan Adams 		    proto->ml_size == diff->ml_size) {
259*56f33205SJonathan Adams 			proto = proto->ml_next;
260*56f33205SJonathan Adams 			diff = diff->ml_next;
2617c478bd9Sstevel@tonic-gate 			continue;
2627c478bd9Sstevel@tonic-gate 		}
2637c478bd9Sstevel@tonic-gate 
264*56f33205SJonathan Adams 		p_base = proto->ml_address;
265*56f33205SJonathan Adams 		p_end = p_base + proto->ml_size;
266*56f33205SJonathan Adams 		d_base = diff->ml_address;
267*56f33205SJonathan Adams 		d_end = d_base + diff->ml_size;
2687c478bd9Sstevel@tonic-gate 		/*
269986fd29aSsetje 		 * here p_base < d_end
270986fd29aSsetje 		 * there are 5 cases
2717c478bd9Sstevel@tonic-gate 		 */
2727c478bd9Sstevel@tonic-gate 
273986fd29aSsetje 		/*
274986fd29aSsetje 		 *	d_end
275986fd29aSsetje 		 *	d_base
276986fd29aSsetje 		 *  p_end
277986fd29aSsetje 		 *  p_base
278986fd29aSsetje 		 *
279986fd29aSsetje 		 * apply func to all of proto item
280986fd29aSsetje 		 */
281986fd29aSsetje 		if (p_end <= d_base) {
282*56f33205SJonathan Adams 			(*func)(p_base, proto->ml_size);
283*56f33205SJonathan Adams 			proto = proto->ml_next;
284986fd29aSsetje 			continue;
285986fd29aSsetje 		}
2867c478bd9Sstevel@tonic-gate 
287986fd29aSsetje 		/*
288986fd29aSsetje 		 * ...
289986fd29aSsetje 		 *	d_base
290986fd29aSsetje 		 *  p_base
291986fd29aSsetje 		 *
292986fd29aSsetje 		 * normalize by applying func from p_base to d_base
293986fd29aSsetje 		 */
294986fd29aSsetje 		if (p_base < d_base)
295986fd29aSsetje 			(*func)(p_base, d_base - p_base);
2967c478bd9Sstevel@tonic-gate 
297986fd29aSsetje 		if (p_end <= d_end) {
2987c478bd9Sstevel@tonic-gate 			/*
299986fd29aSsetje 			 *	d_end
300986fd29aSsetje 			 *  p_end
301986fd29aSsetje 			 *	d_base
302986fd29aSsetje 			 *  p_base
303986fd29aSsetje 			 *
304986fd29aSsetje 			 *	-or-
305986fd29aSsetje 			 *
306986fd29aSsetje 			 *	d_end
307986fd29aSsetje 			 *  p_end
308986fd29aSsetje 			 *  p_base
309986fd29aSsetje 			 *	d_base
310986fd29aSsetje 			 *
311986fd29aSsetje 			 * any non-overlapping ranges applied above,
312986fd29aSsetje 			 * so just continue
3137c478bd9Sstevel@tonic-gate 			 */
314*56f33205SJonathan Adams 			proto = proto->ml_next;
315986fd29aSsetje 			continue;
316986fd29aSsetje 		}
317986fd29aSsetje 
318986fd29aSsetje 		/*
319986fd29aSsetje 		 *  p_end
320986fd29aSsetje 		 *	d_end
321986fd29aSsetje 		 *	d_base
322986fd29aSsetje 		 *  p_base
323986fd29aSsetje 		 *
324986fd29aSsetje 		 *	-or-
325986fd29aSsetje 		 *
326986fd29aSsetje 		 *  p_end
327986fd29aSsetje 		 *	d_end
328986fd29aSsetje 		 *  p_base
329986fd29aSsetje 		 *	d_base
330986fd29aSsetje 		 *
331986fd29aSsetje 		 * Find overlapping d_base..d_end ranges, and apply func
332986fd29aSsetje 		 * where no overlap occurs.  Stop when d_base is above
333986fd29aSsetje 		 * p_end
334986fd29aSsetje 		 */
335*56f33205SJonathan Adams 		for (p_base = d_end, diff = diff->ml_next; diff != NULL;
336*56f33205SJonathan Adams 		    p_base = d_end, diff = diff->ml_next) {
337*56f33205SJonathan Adams 			d_base = diff->ml_address;
338*56f33205SJonathan Adams 			d_end = d_base + diff->ml_size;
339986fd29aSsetje 			if (p_end <= d_base) {
340986fd29aSsetje 				(*func)(p_base, p_end - p_base);
341986fd29aSsetje 				break;
342986fd29aSsetje 			} else
343986fd29aSsetje 				(*func)(p_base, d_base - p_base);
3447c478bd9Sstevel@tonic-gate 		}
345986fd29aSsetje 		if (diff == NULL)
346986fd29aSsetje 			(*func)(p_base, p_end - p_base);
347*56f33205SJonathan Adams 		proto = proto->ml_next;
3487c478bd9Sstevel@tonic-gate 	}
3497c478bd9Sstevel@tonic-gate }
3507c478bd9Sstevel@tonic-gate 
351986fd29aSsetje void
sync_memlists(struct memlist * orig,struct memlist * new)352986fd29aSsetje sync_memlists(struct memlist *orig, struct memlist *new)
353986fd29aSsetje {
354986fd29aSsetje 
355986fd29aSsetje 	/*
356986fd29aSsetje 	 * Find pages allocated via prom by looking for
357986fd29aSsetje 	 * pages on orig, but no on new.
358986fd29aSsetje 	 */
359986fd29aSsetje 	diff_memlists(orig, new, less_pages);
360986fd29aSsetje 
361986fd29aSsetje 	/*
362986fd29aSsetje 	 * Find pages free'd via prom by looking for
363986fd29aSsetje 	 * pages on new, but not on orig.
364986fd29aSsetje 	 */
365986fd29aSsetje 	diff_memlists(new, orig, more_pages);
366986fd29aSsetje }
367986fd29aSsetje 
368986fd29aSsetje 
3697c478bd9Sstevel@tonic-gate /*
3707c478bd9Sstevel@tonic-gate  * Find the page number of the highest installed physical
3717c478bd9Sstevel@tonic-gate  * page and the number of pages installed (one cannot be
3727c478bd9Sstevel@tonic-gate  * calculated from the other because memory isn't necessarily
3737c478bd9Sstevel@tonic-gate  * contiguous).
3747c478bd9Sstevel@tonic-gate  */
3757c478bd9Sstevel@tonic-gate void
installed_top_size_memlist_array(prom_memlist_t * list,size_t nelems,pfn_t * topp,pgcnt_t * sumpagesp)3767c478bd9Sstevel@tonic-gate installed_top_size_memlist_array(
377986fd29aSsetje 	prom_memlist_t *list,	/* base of array */
3787c478bd9Sstevel@tonic-gate 	size_t	nelems,		/* number of elements */
3797c478bd9Sstevel@tonic-gate 	pfn_t *topp,		/* return ptr for top value */
3807c478bd9Sstevel@tonic-gate 	pgcnt_t *sumpagesp)	/* return prt for sum of installed pages */
3817c478bd9Sstevel@tonic-gate {
3827c478bd9Sstevel@tonic-gate 	pfn_t top = 0;
3837c478bd9Sstevel@tonic-gate 	pgcnt_t sumpages = 0;
3847c478bd9Sstevel@tonic-gate 	pfn_t highp;		/* high page in a chunk */
3857c478bd9Sstevel@tonic-gate 	size_t i;
3867c478bd9Sstevel@tonic-gate 
387986fd29aSsetje 	for (i = 0; i < nelems; list++, i++) {
388986fd29aSsetje 		highp = (list->addr + list->size - 1) >> PAGESHIFT;
3897c478bd9Sstevel@tonic-gate 		if (top < highp)
3907c478bd9Sstevel@tonic-gate 			top = highp;
391986fd29aSsetje 		sumpages += (list->size >> PAGESHIFT);
3927c478bd9Sstevel@tonic-gate 	}
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 	*topp = top;
3957c478bd9Sstevel@tonic-gate 	*sumpagesp = sumpages;
3967c478bd9Sstevel@tonic-gate }
3977c478bd9Sstevel@tonic-gate 
3987c478bd9Sstevel@tonic-gate /*
3997c478bd9Sstevel@tonic-gate  * Copy a memory list.  Used in startup() to copy boot's
4007c478bd9Sstevel@tonic-gate  * memory lists to the kernel.
4017c478bd9Sstevel@tonic-gate  */
4027c478bd9Sstevel@tonic-gate void
copy_memlist(prom_memlist_t * src,size_t nelems,struct memlist ** dstp)4037c478bd9Sstevel@tonic-gate copy_memlist(
404986fd29aSsetje 	prom_memlist_t	*src,
4057c478bd9Sstevel@tonic-gate 	size_t		nelems,
4067c478bd9Sstevel@tonic-gate 	struct memlist	**dstp)
4077c478bd9Sstevel@tonic-gate {
4087c478bd9Sstevel@tonic-gate 	struct memlist *dst, *prev;
4097c478bd9Sstevel@tonic-gate 	size_t	i;
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	dst = *dstp;
4127c478bd9Sstevel@tonic-gate 	prev = dst;
4137c478bd9Sstevel@tonic-gate 
414986fd29aSsetje 	for (i = 0; i < nelems; src++, i++) {
415*56f33205SJonathan Adams 		dst->ml_address = src->addr;
416*56f33205SJonathan Adams 		dst->ml_size = src->size;
417*56f33205SJonathan Adams 		dst->ml_next = 0;
4187c478bd9Sstevel@tonic-gate 		if (prev == dst) {
419*56f33205SJonathan Adams 			dst->ml_prev = 0;
4207c478bd9Sstevel@tonic-gate 			dst++;
4217c478bd9Sstevel@tonic-gate 		} else {
422*56f33205SJonathan Adams 			dst->ml_prev = prev;
423*56f33205SJonathan Adams 			prev->ml_next = dst;
4247c478bd9Sstevel@tonic-gate 			dst++;
4257c478bd9Sstevel@tonic-gate 			prev++;
4267c478bd9Sstevel@tonic-gate 		}
4277c478bd9Sstevel@tonic-gate 	}
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 	*dstp = dst;
4307c478bd9Sstevel@tonic-gate }
4317c478bd9Sstevel@tonic-gate 
432986fd29aSsetje 
4337c478bd9Sstevel@tonic-gate static struct bootmem_props {
434986fd29aSsetje 	prom_memlist_t	*ptr;
4357c478bd9Sstevel@tonic-gate 	size_t		nelems;		/* actual number of elements */
436986fd29aSsetje 	size_t		maxsize;	/* max buffer */
437986fd29aSsetje } bootmem_props[3];
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate #define	PHYSINSTALLED	0
4407c478bd9Sstevel@tonic-gate #define	PHYSAVAIL	1
4417c478bd9Sstevel@tonic-gate #define	VIRTAVAIL	2
4427c478bd9Sstevel@tonic-gate 
443986fd29aSsetje /*
444986fd29aSsetje  * Comapct contiguous memory list elements
445986fd29aSsetje  */
446986fd29aSsetje static void
compact_promlist(struct bootmem_props * bpp)447986fd29aSsetje compact_promlist(struct bootmem_props *bpp)
4487c478bd9Sstevel@tonic-gate {
449986fd29aSsetje 	int i = 0, j;
450986fd29aSsetje 	struct prom_memlist *pmp = bpp->ptr;
451986fd29aSsetje 
452986fd29aSsetje 	for (;;) {
453986fd29aSsetje 		if (pmp[i].addr + pmp[i].size == pmp[i+1].addr) {
454986fd29aSsetje 			pmp[i].size += pmp[i+1].size;
455986fd29aSsetje 			bpp->nelems--;
456986fd29aSsetje 			for (j = i + 1; j < bpp->nelems; j++)
457986fd29aSsetje 				pmp[j] = pmp[j+1];
458986fd29aSsetje 			pmp[j].addr = 0;
459986fd29aSsetje 		} else
460986fd29aSsetje 			i++;
461986fd29aSsetje 		if (i == bpp->nelems)
462986fd29aSsetje 			break;
463986fd29aSsetje 	}
464986fd29aSsetje }
465986fd29aSsetje 
466986fd29aSsetje /*
467986fd29aSsetje  *  Sort prom memory lists into ascending order
468986fd29aSsetje  */
469986fd29aSsetje static void
sort_promlist(struct bootmem_props * bpp)470986fd29aSsetje sort_promlist(struct bootmem_props *bpp)
471986fd29aSsetje {
472986fd29aSsetje 	int i, j, min;
473986fd29aSsetje 	struct prom_memlist *pmp = bpp->ptr;
474986fd29aSsetje 	struct prom_memlist temp;
475986fd29aSsetje 
476986fd29aSsetje 	for (i = 0; i < bpp->nelems; i++) {
477986fd29aSsetje 		min = i;
478986fd29aSsetje 
479986fd29aSsetje 		for (j = i+1; j < bpp->nelems; j++)  {
480986fd29aSsetje 			if (pmp[j].addr < pmp[min].addr)
481986fd29aSsetje 				min = j;
4827c478bd9Sstevel@tonic-gate 		}
4837c478bd9Sstevel@tonic-gate 
484986fd29aSsetje 		if (i != min)  {
485986fd29aSsetje 			/* Swap pmp[i] and pmp[min] */
486986fd29aSsetje 			temp = pmp[min];
487986fd29aSsetje 			pmp[min] = pmp[i];
488986fd29aSsetje 			pmp[i] = temp;
489986fd29aSsetje 		}
4907c478bd9Sstevel@tonic-gate 	}
491986fd29aSsetje }
492986fd29aSsetje 
493986fd29aSsetje static int max_bootlist_sz;
494986fd29aSsetje 
495986fd29aSsetje void
init_boot_memlists(void)496986fd29aSsetje init_boot_memlists(void)
497986fd29aSsetje {
498986fd29aSsetje 	size_t	size, len;
499986fd29aSsetje 	char *start;
500986fd29aSsetje 	struct bootmem_props *tmp;
501986fd29aSsetje 
5027c478bd9Sstevel@tonic-gate 	/*
503986fd29aSsetje 	 * These lists can get fragmented as the prom allocates
504986fd29aSsetje 	 * memory, so generously round up.
5057c478bd9Sstevel@tonic-gate 	 */
506986fd29aSsetje 	size = prom_phys_installed_len() + prom_phys_avail_len() +
507986fd29aSsetje 	    prom_virt_avail_len();
508986fd29aSsetje 	size *= 4;
509986fd29aSsetje 	size = roundup(size, PAGESIZE);
510986fd29aSsetje 	start = prom_alloc(0, size, BO_NO_ALIGN);
511986fd29aSsetje 
512986fd29aSsetje 	/*
513986fd29aSsetje 	 * Get physinstalled
514986fd29aSsetje 	 */
515986fd29aSsetje 	tmp = &bootmem_props[PHYSINSTALLED];
516986fd29aSsetje 	len = prom_phys_installed_len();
517986fd29aSsetje 	if (len == 0)
518986fd29aSsetje 		panic("no \"reg\" in /memory");
519986fd29aSsetje 	tmp->nelems = len / sizeof (struct prom_memlist);
520986fd29aSsetje 	tmp->maxsize = len;
521986fd29aSsetje 	tmp->ptr = (prom_memlist_t *)start;
522986fd29aSsetje 	start += len;
523986fd29aSsetje 	size -= len;
524986fd29aSsetje 	(void) prom_phys_installed((caddr_t)tmp->ptr);
525986fd29aSsetje 	sort_promlist(tmp);
526986fd29aSsetje 	compact_promlist(tmp);
527986fd29aSsetje 
528986fd29aSsetje 	/*
529986fd29aSsetje 	 * Start out giving each half of available space
530986fd29aSsetje 	 */
531986fd29aSsetje 	max_bootlist_sz = size;
532986fd29aSsetje 	len = size / 2;
533986fd29aSsetje 	tmp = &bootmem_props[PHYSAVAIL];
534986fd29aSsetje 	tmp->maxsize = len;
535986fd29aSsetje 	tmp->ptr = (prom_memlist_t *)start;
536986fd29aSsetje 	start += len;
537986fd29aSsetje 
538986fd29aSsetje 	tmp = &bootmem_props[VIRTAVAIL];
539986fd29aSsetje 	tmp->maxsize = len;
540986fd29aSsetje 	tmp->ptr = (prom_memlist_t *)start;
541986fd29aSsetje }
542986fd29aSsetje 
543986fd29aSsetje 
544986fd29aSsetje void
copy_boot_memlists(prom_memlist_t ** physinstalled,size_t * physinstalled_len,prom_memlist_t ** physavail,size_t * physavail_len,prom_memlist_t ** virtavail,size_t * virtavail_len)545986fd29aSsetje copy_boot_memlists(
546986fd29aSsetje     prom_memlist_t **physinstalled, size_t *physinstalled_len,
547986fd29aSsetje     prom_memlist_t **physavail, size_t *physavail_len,
548986fd29aSsetje     prom_memlist_t **virtavail, size_t *virtavail_len)
549986fd29aSsetje {
550986fd29aSsetje 	size_t	plen, vlen, move = 0;
551986fd29aSsetje 	struct bootmem_props *il, *pl, *vl;
552986fd29aSsetje 
553986fd29aSsetje 	plen = prom_phys_avail_len();
554986fd29aSsetje 	if (plen == 0)
555986fd29aSsetje 		panic("no \"available\" in /memory");
556986fd29aSsetje 	vlen = prom_virt_avail_len();
557986fd29aSsetje 	if (vlen == 0)
558986fd29aSsetje 		panic("no \"available\" in /virtual-memory");
559986fd29aSsetje 	if (plen + vlen > max_bootlist_sz)
560986fd29aSsetje 		panic("ran out of prom_memlist space");
561986fd29aSsetje 
562986fd29aSsetje 	pl = &bootmem_props[PHYSAVAIL];
563986fd29aSsetje 	vl = &bootmem_props[VIRTAVAIL];
564986fd29aSsetje 
565986fd29aSsetje 	/*
566986fd29aSsetje 	 * re-adjust ptrs if needed
567986fd29aSsetje 	 */
568986fd29aSsetje 	if (plen > pl->maxsize) {
569986fd29aSsetje 		/* move virt avail up */
570986fd29aSsetje 		move = plen - pl->maxsize;
571986fd29aSsetje 		pl->maxsize = plen;
572986fd29aSsetje 		vl->ptr += move / sizeof (struct prom_memlist);
573986fd29aSsetje 		vl->maxsize -= move;
574986fd29aSsetje 	} else if (vlen > vl->maxsize) {
575986fd29aSsetje 		/* move virt avail down */
576986fd29aSsetje 		move = vlen - vl->maxsize;
577986fd29aSsetje 		vl->maxsize = vlen;
578986fd29aSsetje 		vl->ptr -= move / sizeof (struct prom_memlist);
579986fd29aSsetje 		pl->maxsize -= move;
5807c478bd9Sstevel@tonic-gate 	}
5817c478bd9Sstevel@tonic-gate 
582986fd29aSsetje 	pl->nelems = plen / sizeof (struct prom_memlist);
583986fd29aSsetje 	vl->nelems = vlen / sizeof (struct prom_memlist);
584986fd29aSsetje 
5857c478bd9Sstevel@tonic-gate 	/* now we can retrieve the properties */
586986fd29aSsetje 	(void) prom_phys_avail((caddr_t)pl->ptr);
587986fd29aSsetje 	(void) prom_virt_avail((caddr_t)vl->ptr);
588986fd29aSsetje 
589986fd29aSsetje 	/* .. and sort them */
590986fd29aSsetje 	sort_promlist(pl);
591986fd29aSsetje 	sort_promlist(vl);
592986fd29aSsetje 
593986fd29aSsetje 	il = &bootmem_props[PHYSINSTALLED];
594986fd29aSsetje 	*physinstalled = il->ptr;
595986fd29aSsetje 	*physinstalled_len = il->nelems;
5967c478bd9Sstevel@tonic-gate 
597986fd29aSsetje 	*physavail = pl->ptr;
598986fd29aSsetje 	*physavail_len = pl->nelems;
5997c478bd9Sstevel@tonic-gate 
600986fd29aSsetje 	*virtavail = vl->ptr;
601986fd29aSsetje 	*virtavail_len = vl->nelems;
6027c478bd9Sstevel@tonic-gate }
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate /*
6067c478bd9Sstevel@tonic-gate  * Find the page number of the highest installed physical
6077c478bd9Sstevel@tonic-gate  * page and the number of pages installed (one cannot be
6087c478bd9Sstevel@tonic-gate  * calculated from the other because memory isn't necessarily
6097c478bd9Sstevel@tonic-gate  * contiguous).
6107c478bd9Sstevel@tonic-gate  */
6117c478bd9Sstevel@tonic-gate void
installed_top_size(struct memlist * list,pfn_t * topp,pgcnt_t * sumpagesp)6127c478bd9Sstevel@tonic-gate installed_top_size(
6137c478bd9Sstevel@tonic-gate 	struct memlist *list,	/* pointer to start of installed list */
6147c478bd9Sstevel@tonic-gate 	pfn_t *topp,		/* return ptr for top value */
6157c478bd9Sstevel@tonic-gate 	pgcnt_t *sumpagesp)	/* return prt for sum of installed pages */
6167c478bd9Sstevel@tonic-gate {
6177c478bd9Sstevel@tonic-gate 	pfn_t top = 0;
6187c478bd9Sstevel@tonic-gate 	pfn_t highp;		/* high page in a chunk */
6197c478bd9Sstevel@tonic-gate 	pgcnt_t sumpages = 0;
6207c478bd9Sstevel@tonic-gate 
621*56f33205SJonathan Adams 	for (; list; list = list->ml_next) {
622*56f33205SJonathan Adams 		highp = (list->ml_address + list->ml_size - 1) >> PAGESHIFT;
6237c478bd9Sstevel@tonic-gate 		if (top < highp)
6247c478bd9Sstevel@tonic-gate 			top = highp;
625*56f33205SJonathan Adams 		sumpages += (uint_t)(list->ml_size >> PAGESHIFT);
6267c478bd9Sstevel@tonic-gate 	}
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 	*topp = top;
6297c478bd9Sstevel@tonic-gate 	*sumpagesp = sumpages;
6307c478bd9Sstevel@tonic-gate }
631