xref: /illumos-gate/usr/src/uts/sun4/os/memlist.c (revision bc37da3a)
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/param.h>
30 #include <sys/sysmacros.h>
31 #include <sys/signal.h>
32 #include <sys/systm.h>
33 #include <sys/user.h>
34 #include <sys/mman.h>
35 #include <sys/class.h>
36 #include <sys/proc.h>
37 #include <sys/procfs.h>
38 #include <sys/kmem.h>
39 #include <sys/cred.h>
40 #include <sys/archsystm.h>
41 #include <sys/machsystm.h>
42 
43 #include <sys/reboot.h>
44 #include <sys/uadmin.h>
45 
46 #include <sys/vfs.h>
47 #include <sys/vnode.h>
48 #include <sys/session.h>
49 #include <sys/ucontext.h>
50 
51 #include <sys/dnlc.h>
52 #include <sys/var.h>
53 #include <sys/cmn_err.h>
54 #include <sys/debug.h>
55 #include <sys/thread.h>
56 #include <sys/vtrace.h>
57 #include <sys/consdev.h>
58 #include <sys/frame.h>
59 #include <sys/stack.h>
60 #include <sys/swap.h>
61 #include <sys/vmparam.h>
62 #include <sys/cpuvar.h>
63 
64 #include <sys/privregs.h>
65 
66 #include <vm/hat.h>
67 #include <vm/anon.h>
68 #include <vm/as.h>
69 #include <vm/page.h>
70 #include <vm/seg.h>
71 #include <vm/seg_kmem.h>
72 #include <vm/seg_map.h>
73 #include <vm/seg_vn.h>
74 
75 #include <sys/exec.h>
76 #include <sys/acct.h>
77 #include <sys/modctl.h>
78 #include <sys/tuneable.h>
79 
80 #include <c2/audit.h>
81 
82 #include <sys/trap.h>
83 #include <sys/sunddi.h>
84 #include <sys/bootconf.h>
85 #include <sys/memlist.h>
86 #include <sys/memlist_plat.h>
87 #include <sys/systeminfo.h>
88 #include <sys/promif.h>
89 #include <sys/prom_plat.h>
90 
91 u_longlong_t	spec_hole_start = 0x80000000000ull;
92 u_longlong_t	spec_hole_end = 0xfffff80000000000ull;
93 
94 pgcnt_t
95 num_phys_pages()
96 {
97 	pgcnt_t npages = 0;
98 	struct memlist *mp;
99 
100 	for (mp = phys_install; mp != NULL; mp = mp->next)
101 		npages += mp->size >> PAGESHIFT;
102 
103 	return (npages);
104 }
105 
106 
107 pgcnt_t
108 size_virtalloc(prom_memlist_t *avail, size_t nelems)
109 {
110 
111 	u_longlong_t	start, end;
112 	pgcnt_t		allocpages = 0;
113 	uint_t		hole_allocated = 0;
114 	uint_t		i;
115 
116 	for (i = 0; i < nelems - 1; i++) {
117 
118 		start = avail[i].addr + avail[i].size;
119 		end = avail[i + 1].addr;
120 
121 		/*
122 		 * Notes:
123 		 *
124 		 * (1) OBP on platforms with US I/II pre-allocates the hole
125 		 * represented by [spec_hole_start, spec_hole_end);
126 		 * pre-allocation is done to make this range unavailable
127 		 * for any allocation.
128 		 *
129 		 * (2) OBP on starcat always pre-allocates the hole similar to
130 		 * platforms with US I/II.
131 		 *
132 		 * (3) OBP on serengeti does _not_ pre-allocate the hole.
133 		 *
134 		 * (4) OBP ignores Spitfire Errata #21; i.e. it does _not_
135 		 * fill up or pre-allocate an additional 4GB on both sides
136 		 * of the hole.
137 		 *
138 		 * (5) kernel virtual range [spec_hole_start, spec_hole_end)
139 		 * is _not_ used on any platform including those with
140 		 * UltraSPARC III where there is no hole.
141 		 *
142 		 * Algorithm:
143 		 *
144 		 * Check if range [spec_hole_start, spec_hole_end) is
145 		 * pre-allocated by OBP; if so, subtract that range from
146 		 * allocpages.
147 		 */
148 		if (end >= spec_hole_end && start <= spec_hole_start)
149 			hole_allocated = 1;
150 
151 		allocpages += btopr(end - start);
152 	}
153 
154 	if (hole_allocated)
155 		allocpages -= btop(spec_hole_end - spec_hole_start);
156 
157 	return (allocpages);
158 }
159 
160 /*
161  * Returns the max contiguous physical memory present in the
162  * memlist "physavail".
163  */
164 uint64_t
165 get_max_phys_size(
166 	struct memlist	*physavail)
167 {
168 	uint64_t	max_size = 0;
169 
170 	for (; physavail; physavail = physavail->next) {
171 		if (physavail->size > max_size)
172 			max_size = physavail->size;
173 	}
174 
175 	return (max_size);
176 }
177 
178 
179 
180 struct vnode prom_ppages;
181 
182 static void
183 more_pages(uint64_t base, uint64_t len)
184 {
185 	void kphysm_add();
186 
187 	kphysm_add(base, len, 1);
188 }
189 
190 static void
191 less_pages(uint64_t base, uint64_t len)
192 {
193 	uint64_t pa, end = base + len;
194 	extern int kcage_on;
195 
196 	for (pa = base; pa < end; pa += PAGESIZE) {
197 		pfn_t pfnum;
198 		page_t *pp;
199 
200 		pfnum = (pfn_t)(pa >> PAGESHIFT);
201 		if ((pp = page_numtopp_nolock(pfnum)) == NULL)
202 			cmn_err(CE_PANIC, "missing pfnum %lx", pfnum);
203 
204 		/*
205 		 * must break up any large pages that may have
206 		 * constituent pages being utilized for
207 		 * prom_alloc()'s. page_reclaim() can't handle
208 		 * large pages.
209 		 */
210 		if (pp->p_szc != 0)
211 			page_boot_demote(pp);
212 
213 		if (!PAGE_LOCKED(pp) && pp->p_lckcnt == 0) {
214 			/*
215 			 * Ahhh yes, a prom page,
216 			 * suck it off the freelist,
217 			 * lock it, and hashin on prom_pages vp.
218 			 */
219 			if (page_trylock(pp, SE_EXCL) == 0)
220 				cmn_err(CE_PANIC, "prom page locked");
221 
222 			(void) page_reclaim(pp, NULL);
223 			/*
224 			 * vnode offsets on the prom_ppages vnode
225 			 * are page numbers (gack) for >32 bit
226 			 * physical memory machines.
227 			 */
228 			(void) page_hashin(pp, &prom_ppages,
229 			    (offset_t)pfnum, NULL);
230 
231 			if (kcage_on) {
232 				ASSERT(pp->p_szc == 0);
233 				PP_SETNORELOC(pp);
234 			}
235 			(void) page_pp_lock(pp, 0, 1);
236 		}
237 	}
238 }
239 
240 void
241 diff_memlists(struct memlist *proto, struct memlist *diff, void (*func)())
242 {
243 	uint64_t p_base, p_end, d_base, d_end;
244 
245 	while (proto != NULL) {
246 		/*
247 		 * find diff item which may overlap with proto item
248 		 * if none, apply func to all of proto item
249 		 */
250 		while (diff != NULL &&
251 		    proto->address >= diff->address + diff->size)
252 			diff = diff->next;
253 		if (diff == NULL) {
254 			(*func)(proto->address, proto->size);
255 			proto = proto->next;
256 			continue;
257 		}
258 		if (proto->address == diff->address &&
259 		    proto->size == diff->size) {
260 			proto = proto->next;
261 			diff = diff->next;
262 			continue;
263 		}
264 
265 		p_base = proto->address;
266 		p_end = p_base + proto->size;
267 		d_base = diff->address;
268 		d_end = d_base + diff->size;
269 		/*
270 		 * here p_base < d_end
271 		 * there are 5 cases
272 		 */
273 
274 		/*
275 		 *	d_end
276 		 *	d_base
277 		 *  p_end
278 		 *  p_base
279 		 *
280 		 * apply func to all of proto item
281 		 */
282 		if (p_end <= d_base) {
283 			(*func)(p_base, proto->size);
284 			proto = proto->next;
285 			continue;
286 		}
287 
288 		/*
289 		 * ...
290 		 *	d_base
291 		 *  p_base
292 		 *
293 		 * normalize by applying func from p_base to d_base
294 		 */
295 		if (p_base < d_base)
296 			(*func)(p_base, d_base - p_base);
297 
298 		if (p_end <= d_end) {
299 			/*
300 			 *	d_end
301 			 *  p_end
302 			 *	d_base
303 			 *  p_base
304 			 *
305 			 *	-or-
306 			 *
307 			 *	d_end
308 			 *  p_end
309 			 *  p_base
310 			 *	d_base
311 			 *
312 			 * any non-overlapping ranges applied above,
313 			 * so just continue
314 			 */
315 			proto = proto->next;
316 			continue;
317 		}
318 
319 		/*
320 		 *  p_end
321 		 *	d_end
322 		 *	d_base
323 		 *  p_base
324 		 *
325 		 *	-or-
326 		 *
327 		 *  p_end
328 		 *	d_end
329 		 *  p_base
330 		 *	d_base
331 		 *
332 		 * Find overlapping d_base..d_end ranges, and apply func
333 		 * where no overlap occurs.  Stop when d_base is above
334 		 * p_end
335 		 */
336 		for (p_base = d_end, diff = diff->next; diff != NULL;
337 		    p_base = d_end, diff = diff->next) {
338 			d_base = diff->address;
339 			d_end = d_base + diff->size;
340 			if (p_end <= d_base) {
341 				(*func)(p_base, p_end - p_base);
342 				break;
343 			} else
344 				(*func)(p_base, d_base - p_base);
345 		}
346 		if (diff == NULL)
347 			(*func)(p_base, p_end - p_base);
348 		proto = proto->next;
349 	}
350 }
351 
352 void
353 sync_memlists(struct memlist *orig, struct memlist *new)
354 {
355 
356 	/*
357 	 * Find pages allocated via prom by looking for
358 	 * pages on orig, but no on new.
359 	 */
360 	diff_memlists(orig, new, less_pages);
361 
362 	/*
363 	 * Find pages free'd via prom by looking for
364 	 * pages on new, but not on orig.
365 	 */
366 	diff_memlists(new, orig, more_pages);
367 }
368 
369 
370 /*
371  * Find the page number of the highest installed physical
372  * page and the number of pages installed (one cannot be
373  * calculated from the other because memory isn't necessarily
374  * contiguous).
375  */
376 void
377 installed_top_size_memlist_array(
378 	prom_memlist_t *list,	/* base of array */
379 	size_t	nelems,		/* number of elements */
380 	pfn_t *topp,		/* return ptr for top value */
381 	pgcnt_t *sumpagesp)	/* return prt for sum of installed pages */
382 {
383 	pfn_t top = 0;
384 	pgcnt_t sumpages = 0;
385 	pfn_t highp;		/* high page in a chunk */
386 	size_t i;
387 
388 	for (i = 0; i < nelems; list++, i++) {
389 		highp = (list->addr + list->size - 1) >> PAGESHIFT;
390 		if (top < highp)
391 			top = highp;
392 		sumpages += (list->size >> PAGESHIFT);
393 	}
394 
395 	*topp = top;
396 	*sumpagesp = sumpages;
397 }
398 
399 /*
400  * Copy a memory list.  Used in startup() to copy boot's
401  * memory lists to the kernel.
402  */
403 void
404 copy_memlist(
405 	prom_memlist_t	*src,
406 	size_t		nelems,
407 	struct memlist	**dstp)
408 {
409 	struct memlist *dst, *prev;
410 	size_t	i;
411 
412 	dst = *dstp;
413 	prev = dst;
414 
415 	for (i = 0; i < nelems; src++, i++) {
416 		dst->address = src->addr;
417 		dst->size = src->size;
418 		dst->next = 0;
419 		if (prev == dst) {
420 			dst->prev = 0;
421 			dst++;
422 		} else {
423 			dst->prev = prev;
424 			prev->next = dst;
425 			dst++;
426 			prev++;
427 		}
428 	}
429 
430 	*dstp = dst;
431 }
432 
433 
434 static struct bootmem_props {
435 	prom_memlist_t	*ptr;
436 	size_t		nelems;		/* actual number of elements */
437 	size_t		maxsize;	/* max buffer */
438 } bootmem_props[3];
439 
440 #define	PHYSINSTALLED	0
441 #define	PHYSAVAIL	1
442 #define	VIRTAVAIL	2
443 
444 /*
445  * Comapct contiguous memory list elements
446  */
447 static void
448 compact_promlist(struct bootmem_props *bpp)
449 {
450 	int i = 0, j;
451 	struct prom_memlist *pmp = bpp->ptr;
452 
453 	for (;;) {
454 		if (pmp[i].addr + pmp[i].size == pmp[i+1].addr) {
455 			pmp[i].size += pmp[i+1].size;
456 			bpp->nelems--;
457 			for (j = i + 1; j < bpp->nelems; j++)
458 				pmp[j] = pmp[j+1];
459 			pmp[j].addr = 0;
460 		} else
461 			i++;
462 		if (i == bpp->nelems)
463 			break;
464 	}
465 }
466 
467 /*
468  *  Sort prom memory lists into ascending order
469  */
470 static void
471 sort_promlist(struct bootmem_props *bpp)
472 {
473 	int i, j, min;
474 	struct prom_memlist *pmp = bpp->ptr;
475 	struct prom_memlist temp;
476 
477 	for (i = 0; i < bpp->nelems; i++) {
478 		min = i;
479 
480 		for (j = i+1; j < bpp->nelems; j++)  {
481 			if (pmp[j].addr < pmp[min].addr)
482 				min = j;
483 		}
484 
485 		if (i != min)  {
486 			/* Swap pmp[i] and pmp[min] */
487 			temp = pmp[min];
488 			pmp[min] = pmp[i];
489 			pmp[i] = temp;
490 		}
491 	}
492 }
493 
494 static int max_bootlist_sz;
495 
496 void
497 init_boot_memlists(void)
498 {
499 	size_t	size, len;
500 	char *start;
501 	struct bootmem_props *tmp;
502 
503 	/*
504 	 * These lists can get fragmented as the prom allocates
505 	 * memory, so generously round up.
506 	 */
507 	size = prom_phys_installed_len() + prom_phys_avail_len() +
508 	    prom_virt_avail_len();
509 	size *= 4;
510 	size = roundup(size, PAGESIZE);
511 	start = prom_alloc(0, size, BO_NO_ALIGN);
512 
513 	/*
514 	 * Get physinstalled
515 	 */
516 	tmp = &bootmem_props[PHYSINSTALLED];
517 	len = prom_phys_installed_len();
518 	if (len == 0)
519 		panic("no \"reg\" in /memory");
520 	tmp->nelems = len / sizeof (struct prom_memlist);
521 	tmp->maxsize = len;
522 	tmp->ptr = (prom_memlist_t *)start;
523 	start += len;
524 	size -= len;
525 	(void) prom_phys_installed((caddr_t)tmp->ptr);
526 	sort_promlist(tmp);
527 	compact_promlist(tmp);
528 
529 	/*
530 	 * Start out giving each half of available space
531 	 */
532 	max_bootlist_sz = size;
533 	len = size / 2;
534 	tmp = &bootmem_props[PHYSAVAIL];
535 	tmp->maxsize = len;
536 	tmp->ptr = (prom_memlist_t *)start;
537 	start += len;
538 
539 	tmp = &bootmem_props[VIRTAVAIL];
540 	tmp->maxsize = len;
541 	tmp->ptr = (prom_memlist_t *)start;
542 }
543 
544 
545 void
546 copy_boot_memlists(
547     prom_memlist_t **physinstalled, size_t *physinstalled_len,
548     prom_memlist_t **physavail, size_t *physavail_len,
549     prom_memlist_t **virtavail, size_t *virtavail_len)
550 {
551 	size_t	plen, vlen, move = 0;
552 	struct bootmem_props *il, *pl, *vl;
553 
554 	plen = prom_phys_avail_len();
555 	if (plen == 0)
556 		panic("no \"available\" in /memory");
557 	vlen = prom_virt_avail_len();
558 	if (vlen == 0)
559 		panic("no \"available\" in /virtual-memory");
560 	if (plen + vlen > max_bootlist_sz)
561 		panic("ran out of prom_memlist space");
562 
563 	pl = &bootmem_props[PHYSAVAIL];
564 	vl = &bootmem_props[VIRTAVAIL];
565 
566 	/*
567 	 * re-adjust ptrs if needed
568 	 */
569 	if (plen > pl->maxsize) {
570 		/* move virt avail up */
571 		move = plen - pl->maxsize;
572 		pl->maxsize = plen;
573 		vl->ptr += move / sizeof (struct prom_memlist);
574 		vl->maxsize -= move;
575 	} else if (vlen > vl->maxsize) {
576 		/* move virt avail down */
577 		move = vlen - vl->maxsize;
578 		vl->maxsize = vlen;
579 		vl->ptr -= move / sizeof (struct prom_memlist);
580 		pl->maxsize -= move;
581 	}
582 
583 	pl->nelems = plen / sizeof (struct prom_memlist);
584 	vl->nelems = vlen / sizeof (struct prom_memlist);
585 
586 	/* now we can retrieve the properties */
587 	(void) prom_phys_avail((caddr_t)pl->ptr);
588 	(void) prom_virt_avail((caddr_t)vl->ptr);
589 
590 	/* .. and sort them */
591 	sort_promlist(pl);
592 	sort_promlist(vl);
593 
594 	il = &bootmem_props[PHYSINSTALLED];
595 	*physinstalled = il->ptr;
596 	*physinstalled_len = il->nelems;
597 
598 	*physavail = pl->ptr;
599 	*physavail_len = pl->nelems;
600 
601 	*virtavail = vl->ptr;
602 	*virtavail_len = vl->nelems;
603 }
604 
605 
606 /*
607  * Find the page number of the highest installed physical
608  * page and the number of pages installed (one cannot be
609  * calculated from the other because memory isn't necessarily
610  * contiguous).
611  */
612 void
613 installed_top_size(
614 	struct memlist *list,	/* pointer to start of installed list */
615 	pfn_t *topp,		/* return ptr for top value */
616 	pgcnt_t *sumpagesp)	/* return prt for sum of installed pages */
617 {
618 	pfn_t top = 0;
619 	pfn_t highp;		/* high page in a chunk */
620 	pgcnt_t sumpages = 0;
621 
622 	for (; list; list = list->next) {
623 		highp = (list->address + list->size - 1) >> PAGESHIFT;
624 		if (top < highp)
625 			top = highp;
626 		sumpages += (uint_t)(list->size >> PAGESHIFT);
627 	}
628 
629 	*topp = top;
630 	*sumpagesp = sumpages;
631 }
632