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