#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/signal.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/mman.h>
#include <sys/class.h>
#include <sys/proc.h>
#include <sys/procfs.h>
#include <sys/kmem.h>
#include <sys/cred.h>
#include <sys/archsystm.h>
#include <sys/machsystm.h>
#include <sys/reboot.h>
#include <sys/uadmin.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/session.h>
#include <sys/ucontext.h>
#include <sys/dnlc.h>
#include <sys/var.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/thread.h>
#include <sys/vtrace.h>
#include <sys/consdev.h>
#include <sys/frame.h>
#include <sys/stack.h>
#include <sys/swap.h>
#include <sys/vmparam.h>
#include <sys/cpuvar.h>
#include <sys/privregs.h>
#include <vm/hat.h>
#include <vm/anon.h>
#include <vm/as.h>
#include <vm/page.h>
#include <vm/seg.h>
#include <vm/seg_kmem.h>
#include <vm/seg_map.h>
#include <vm/seg_vn.h>
#include <vm/vm_dep.h>
#include <sys/exec.h>
#include <sys/acct.h>
#include <sys/modctl.h>
#include <sys/tuneable.h>
#include <c2/audit.h>
#include <sys/trap.h>
#include <sys/sunddi.h>
#include <sys/bootconf.h>
#include <sys/memlist.h>
#include <sys/memlist_plat.h>
#include <sys/systeminfo.h>
#include <sys/promif.h>
#include <sys/prom_plat.h>
u_longlong_t spec_hole_start = 0x80000000000ull;
u_longlong_t spec_hole_end = 0xfffff80000000000ull;
pgcnt_t
num_phys_pages()
{
pgcnt_t npages = 0;
struct memlist *mp;
for (mp = phys_install; mp != NULL; mp = mp->ml_next)
npages += mp->ml_size >> PAGESHIFT;
return (npages);
}
pgcnt_t
size_virtalloc(prom_memlist_t *avail, size_t nelems)
{
u_longlong_t start, end;
pgcnt_t allocpages = 0;
uint_t hole_allocated = 0;
uint_t i;
for (i = 0; i < nelems - 1; i++) {
start = avail[i].addr + avail[i].size;
end = avail[i + 1].addr;
if (end >= spec_hole_end && start <= spec_hole_start)
hole_allocated = 1;
allocpages += btopr(end - start);
}
if (hole_allocated)
allocpages -= btop(spec_hole_end - spec_hole_start);
return (allocpages);
}
uint64_t
get_max_phys_size(
struct memlist *physavail)
{
uint64_t max_size = 0;
for (; physavail; physavail = physavail->ml_next) {
if (physavail->ml_size > max_size)
max_size = physavail->ml_size;
}
return (max_size);
}
static void
more_pages(uint64_t base, uint64_t len)
{
void kphysm_add();
kphysm_add(base, len, 1);
}
static void
less_pages(uint64_t base, uint64_t len)
{
uint64_t pa, end = base + len;
extern int kcage_on;
for (pa = base; pa < end; pa += PAGESIZE) {
pfn_t pfnum;
page_t *pp;
pfnum = (pfn_t)(pa >> PAGESHIFT);
if ((pp = page_numtopp_nolock(pfnum)) == NULL)
cmn_err(CE_PANIC, "missing pfnum %lx", pfnum);
if (pp->p_szc != 0)
page_boot_demote(pp);
if (!PAGE_LOCKED(pp) && pp->p_lckcnt == 0) {
if (page_trylock(pp, SE_EXCL) == 0)
cmn_err(CE_PANIC, "prom page locked");
(void) page_reclaim(pp, NULL);
(void) page_hashin(pp, &promvp,
(offset_t)pfnum, NULL);
if (kcage_on) {
ASSERT(pp->p_szc == 0);
if (PP_ISNORELOC(pp) == 0) {
PP_SETNORELOC(pp);
PLCNT_XFER_NORELOC(pp);
}
}
(void) page_pp_lock(pp, 0, 1);
}
}
}
void
diff_memlists(struct memlist *proto, struct memlist *diff, void (*func)())
{
uint64_t p_base, p_end, d_base, d_end;
while (proto != NULL) {
while (diff != NULL &&
proto->ml_address >= diff->ml_address + diff->ml_size)
diff = diff->ml_next;
if (diff == NULL) {
(*func)(proto->ml_address, proto->ml_size);
proto = proto->ml_next;
continue;
}
if (proto->ml_address == diff->ml_address &&
proto->ml_size == diff->ml_size) {
proto = proto->ml_next;
diff = diff->ml_next;
continue;
}
p_base = proto->ml_address;
p_end = p_base + proto->ml_size;
d_base = diff->ml_address;
d_end = d_base + diff->ml_size;
if (p_end <= d_base) {
(*func)(p_base, proto->ml_size);
proto = proto->ml_next;
continue;
}
if (p_base < d_base)
(*func)(p_base, d_base - p_base);
if (p_end <= d_end) {
proto = proto->ml_next;
continue;
}
for (p_base = d_end, diff = diff->ml_next; diff != NULL;
p_base = d_end, diff = diff->ml_next) {
d_base = diff->ml_address;
d_end = d_base + diff->ml_size;
if (p_end <= d_base) {
(*func)(p_base, p_end - p_base);
break;
} else
(*func)(p_base, d_base - p_base);
}
if (diff == NULL)
(*func)(p_base, p_end - p_base);
proto = proto->ml_next;
}
}
void
sync_memlists(struct memlist *orig, struct memlist *new)
{
diff_memlists(orig, new, less_pages);
diff_memlists(new, orig, more_pages);
}
void
installed_top_size_memlist_array(
prom_memlist_t *list,
size_t nelems,
pfn_t *topp,
pgcnt_t *sumpagesp)
{
pfn_t top = 0;
pgcnt_t sumpages = 0;
pfn_t highp;
size_t i;
for (i = 0; i < nelems; list++, i++) {
highp = (list->addr + list->size - 1) >> PAGESHIFT;
if (top < highp)
top = highp;
sumpages += (list->size >> PAGESHIFT);
}
*topp = top;
*sumpagesp = sumpages;
}
void
copy_memlist(
prom_memlist_t *src,
size_t nelems,
struct memlist **dstp)
{
struct memlist *dst, *prev;
size_t i;
dst = *dstp;
prev = dst;
for (i = 0; i < nelems; src++, i++) {
dst->ml_address = src->addr;
dst->ml_size = src->size;
dst->ml_next = 0;
if (prev == dst) {
dst->ml_prev = 0;
dst++;
} else {
dst->ml_prev = prev;
prev->ml_next = dst;
dst++;
prev++;
}
}
*dstp = dst;
}
static struct bootmem_props {
prom_memlist_t *ptr;
size_t nelems;
size_t maxsize;
} bootmem_props[3];
#define PHYSINSTALLED 0
#define PHYSAVAIL 1
#define VIRTAVAIL 2
static void
compact_promlist(struct bootmem_props *bpp)
{
int i = 0, j;
struct prom_memlist *pmp = bpp->ptr;
for (;;) {
if (pmp[i].addr + pmp[i].size == pmp[i+1].addr) {
pmp[i].size += pmp[i+1].size;
bpp->nelems--;
for (j = i + 1; j < bpp->nelems; j++)
pmp[j] = pmp[j+1];
pmp[j].addr = 0;
} else
i++;
if (i == bpp->nelems)
break;
}
}
static void
sort_promlist(struct bootmem_props *bpp)
{
int i, j, min;
struct prom_memlist *pmp = bpp->ptr;
struct prom_memlist temp;
for (i = 0; i < bpp->nelems; i++) {
min = i;
for (j = i+1; j < bpp->nelems; j++) {
if (pmp[j].addr < pmp[min].addr)
min = j;
}
if (i != min) {
temp = pmp[min];
pmp[min] = pmp[i];
pmp[i] = temp;
}
}
}
static int max_bootlist_sz;
void
init_boot_memlists(void)
{
size_t size, len;
char *start;
struct bootmem_props *tmp;
size = prom_phys_installed_len() + prom_phys_avail_len() +
prom_virt_avail_len();
size *= 4;
size = roundup(size, PAGESIZE);
start = prom_alloc(0, size, BO_NO_ALIGN);
tmp = &bootmem_props[PHYSINSTALLED];
len = prom_phys_installed_len();
if (len == 0)
panic("no \"reg\" in /memory");
tmp->nelems = len / sizeof (struct prom_memlist);
tmp->maxsize = len;
tmp->ptr = (prom_memlist_t *)start;
start += len;
size -= len;
(void) prom_phys_installed((caddr_t)tmp->ptr);
sort_promlist(tmp);
compact_promlist(tmp);
max_bootlist_sz = size;
len = size / 2;
tmp = &bootmem_props[PHYSAVAIL];
tmp->maxsize = len;
tmp->ptr = (prom_memlist_t *)start;
start += len;
tmp = &bootmem_props[VIRTAVAIL];
tmp->maxsize = len;
tmp->ptr = (prom_memlist_t *)start;
}
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)
{
size_t plen, vlen, move = 0;
struct bootmem_props *il, *pl, *vl;
plen = prom_phys_avail_len();
if (plen == 0)
panic("no \"available\" in /memory");
vlen = prom_virt_avail_len();
if (vlen == 0)
panic("no \"available\" in /virtual-memory");
if (plen + vlen > max_bootlist_sz)
panic("ran out of prom_memlist space");
pl = &bootmem_props[PHYSAVAIL];
vl = &bootmem_props[VIRTAVAIL];
if (plen > pl->maxsize) {
move = plen - pl->maxsize;
pl->maxsize = plen;
vl->ptr += move / sizeof (struct prom_memlist);
vl->maxsize -= move;
} else if (vlen > vl->maxsize) {
move = vlen - vl->maxsize;
vl->maxsize = vlen;
vl->ptr -= move / sizeof (struct prom_memlist);
pl->maxsize -= move;
}
pl->nelems = plen / sizeof (struct prom_memlist);
vl->nelems = vlen / sizeof (struct prom_memlist);
(void) prom_phys_avail((caddr_t)pl->ptr);
(void) prom_virt_avail((caddr_t)vl->ptr);
sort_promlist(pl);
sort_promlist(vl);
il = &bootmem_props[PHYSINSTALLED];
*physinstalled = il->ptr;
*physinstalled_len = il->nelems;
*physavail = pl->ptr;
*physavail_len = pl->nelems;
*virtavail = vl->ptr;
*virtavail_len = vl->nelems;
}
void
installed_top_size(
struct memlist *list,
pfn_t *topp,
pgcnt_t *sumpagesp)
{
pfn_t top = 0;
pfn_t highp;
pgcnt_t sumpages = 0;
for (; list; list = list->ml_next) {
highp = (list->ml_address + list->ml_size - 1) >> PAGESHIFT;
if (top < highp)
top = highp;
sumpages += (uint_t)(list->ml_size >> PAGESHIFT);
}
*topp = top;
*sumpagesp = sumpages;
}