#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/cpr.h>
#include <sys/fs/ufs_fs.h>
#include <sys/prom_plat.h>
#include "cprboot.h"
#define PA_BUFSIZE 1024
#define CB_SETBIT 1
#define CB_ISSET 2
#define CB_ISCLR 3
int cb_nbitmaps;
static arange_t *cb_physavail;
static char pabuf[PA_BUFSIZE];
static caddr_t high_virt;
static cbd_t cb_bmda[CPR_MAX_BMDESC];
static int tracking_init;
static int
cb_bitop(pfn_t ppn, int op)
{
int rel, rval = 0;
char *bitmap;
cbd_t *dp;
for (dp = cb_bmda; dp->cbd_size; dp++) {
if (PPN_IN_RANGE(ppn, dp)) {
bitmap = (char *)dp->cbd_reg_bitmap;
rel = ppn - dp->cbd_spfn;
if (op == CB_SETBIT)
setbit(bitmap, rel);
else if (op == CB_ISSET)
rval = isset(bitmap, rel);
else if (op == CB_ISCLR)
rval = isclr(bitmap, rel);
break;
}
}
return (rval);
}
static void
count_free_pages(void)
{
arange_t *arp;
pfn_t bitno;
int cnt;
for (arp = cb_physavail; arp->high; arp++) {
cnt = 0;
for (bitno = arp->low; bitno <= arp->high; bitno++) {
if (cb_bitop(bitno, CB_ISCLR))
cnt++;
}
arp->nfree = cnt;
}
}
static pfn_t
search_phav_pages(void)
{
static arange_t *arp;
static pfn_t bitno;
int rescan;
if (arp == NULL) {
count_free_pages();
arp = cb_physavail;
bitno = arp->low;
}
for (rescan = 0; rescan < 2; rescan++) {
for (; arp->high; bitno = (++arp)->low) {
if (arp->nfree == 0)
continue;
for (; bitno <= arp->high; bitno++) {
if (cb_bitop(bitno, CB_ISCLR)) {
(void) cb_bitop(bitno, CB_SETBIT);
arp->nfree--;
return (bitno++);
}
}
}
arp = cb_physavail;
bitno = arp->low;
}
return (PFN_INVALID);
}
static pfn_t
search_buf_pages(void)
{
size_t coff, src_base;
static size_t lboff;
pfn_t ppn;
if (tracking_init == 0)
return (PFN_INVALID);
src_base = sfile.buf_offset & MMU_PAGEMASK;
while (lboff < src_base) {
coff = lboff;
lboff += MMU_PAGESIZE;
ppn = SF_ORIG_PPN(coff);
if (cb_bitop(ppn, CB_ISCLR)) {
(void) cb_bitop(ppn, CB_SETBIT);
SF_STAT_INC(recycle);
return (ppn);
} else if (SF_DIFF_PPN(coff)) {
SF_STAT_INC(recycle);
return (SF_BUF_PPN(coff));
}
}
return (PFN_INVALID);
}
pfn_t
find_apage(void)
{
pfn_t ppn;
ppn = search_phav_pages();
if (ppn != PFN_INVALID)
return (ppn);
ppn = search_buf_pages();
if (ppn != PFN_INVALID)
return (ppn);
prom_printf("\n%s: ran out of available/free pages!\n%s\n",
prog, rsvp);
cb_exit_to_mon();
return (PFN_INVALID);
}
static caddr_t
map_free_phys(caddr_t vaddr, size_t size, char *name)
{
int pages, ppn, err;
physaddr_t phys;
caddr_t virt;
char *str;
str = "map_free_phys";
virt = prom_claim_virt(size, vaddr);
CB_VPRINTF(("\n%s: claim vaddr 0x%p, size 0x%lx, ret 0x%p\n",
str, (void *)vaddr, size, (void *)virt));
if (virt != vaddr) {
prom_printf("\n%s: cant reserve (0x%p - 0x%p) for \"%s\"\n",
str, (void *)vaddr, (void *)(vaddr + size), name);
return (virt);
}
for (pages = mmu_btop(size); pages--; virt += MMU_PAGESIZE) {
ppn = find_apage();
phys = PN_TO_ADDR(ppn);
err = prom_map_phys(-1, MMU_PAGESIZE, virt, phys);
if (err || verbose) {
prom_printf(" map virt 0x%p, phys 0x%llx, "
"ppn 0x%x, ret %d\n", (void *)virt, phys, ppn, err);
}
if (err)
return ((caddr_t)ERR);
}
return (vaddr);
}
int
cb_set_bitmap(void)
{
size_t bmda_size, all_bitmap_size, alloc_size;
caddr_t newvirt, src, dst, base;
cbd_t *dp;
char *str;
str = "cb_set_bitmap";
CB_VPRINTF((ent_fmt, str, entry));
if (cb_nbitmaps > (CPR_MAX_BMDESC - 1)) {
prom_printf("%s: too many bitmap descriptors %d, max %d\n",
str, cb_nbitmaps, (CPR_MAX_BMDESC - 1));
return (ERR);
}
bmda_size = cb_nbitmaps * sizeof (cbd_t);
src = SF_DATA();
bcopy(src, cb_bmda, bmda_size);
base = src + bmda_size;
all_bitmap_size = 0;
for (dp = cb_bmda; dp < &cb_bmda[cb_nbitmaps]; dp++) {
if (dp->cbd_magic != CPR_BITMAP_MAGIC) {
prom_printf("%s: bad magic 0x%x, expect 0x%x\n",
str, dp->cbd_magic, CPR_BITMAP_MAGIC);
return (ERR);
}
all_bitmap_size += dp->cbd_size;
dp->cbd_reg_bitmap = (cpr_ptr)base;
base += dp->cbd_size;
}
alloc_size = PAGE_ROUNDUP(all_bitmap_size);
if (verbose || CPR_DBG(7)) {
prom_printf("%s: nbitmaps %d, bmda_size 0x%lx\n",
str, cb_nbitmaps, bmda_size);
prom_printf("%s: all_bitmap_size 0x%lx, alloc_size 0x%lx\n",
str, all_bitmap_size, alloc_size);
}
high_virt = (caddr_t)CB_HIGH_VIRT;
newvirt = map_free_phys(high_virt, alloc_size, "bitmaps");
if (newvirt != high_virt)
return (ERR);
base = src + bmda_size;
dst = newvirt;
bcopy(base, dst, all_bitmap_size);
if (alloc_size > all_bitmap_size)
bzero(dst + all_bitmap_size, alloc_size - all_bitmap_size);
for (dp = cb_bmda; dp->cbd_size; dp++) {
dp->cbd_reg_bitmap = (cpr_ptr)dst;
dst += dp->cbd_size;
}
SF_ADV(bmda_size + all_bitmap_size);
high_virt += alloc_size;
return (0);
}
int
cb_get_newstack(void)
{
caddr_t newstack;
CB_VENTRY(cb_get_newstack);
newstack = map_free_phys((caddr_t)CB_STACK_VIRT,
CB_STACK_SIZE, "new stack");
if (newstack != (caddr_t)CB_STACK_VIRT)
return (ERR);
return (0);
}
int
cb_tracking_setup(void)
{
pfn_t ppn, lppn;
uint_t *imap;
caddr_t newvirt;
size_t size;
int pages;
CB_VENTRY(cb_tracking_setup);
pages = mmu_btop(sfile.size);
size = PAGE_ROUNDUP(pages * sizeof (*imap));
newvirt = map_free_phys(high_virt, size, "buf tracking");
if (newvirt != high_virt)
return (ERR);
sfile.buf_map = (uint_t *)newvirt;
high_virt += size;
imap = sfile.buf_map;
lppn = sfile.low_ppn + pages;
for (ppn = sfile.low_ppn; ppn < lppn; ppn++, imap++)
*imap = (uint_t)ppn;
tracking_init = 1;
return (0);
}
int
cb_get_physavail(void)
{
int len, glen, scnt, need, space;
char *str, *pdev, *mem_prop;
pnode_t mem_node;
physaddr_t phys;
pgcnt_t pages;
arange_t *arp;
pphav_t *pap;
size_t size;
pfn_t ppn;
int err;
str = "cb_get_physavail";
CB_VPRINTF((ent_fmt, str, entry));
size = PAGE_ROUNDUP((uintptr_t)_end) - (uintptr_t)_start;
ppn = cpr_vatopfn((caddr_t)_start);
phys = PN_TO_ADDR(ppn);
err = prom_claim_phys(size, phys);
CB_VPRINTF((" text/data claim (0x%lx - 0x%lx) = %d\n",
ppn, ppn + mmu_btop(size) - 1, err));
if (err)
return (ERR);
pdev = "/memory";
mem_node = prom_finddevice(pdev);
if (mem_node == OBP_BADNODE) {
prom_printf("%s: cant find \"%s\" node\n", str, pdev);
return (ERR);
}
mem_prop = "available";
len = prom_getproplen(mem_node, mem_prop);
scnt = len / sizeof (*pap);
need = len + (sizeof (*arp) * (scnt + 1));
space = sizeof (pabuf);
CB_VPRINTF((" %s node 0x%x, len %d\n", pdev, mem_node, len));
if (len == -1 || need > space) {
prom_printf("\n%s: bad \"%s\" length %d, min %d, max %d\n",
str, mem_prop, len, need, space);
return (ERR);
}
glen = prom_getprop(mem_node, mem_prop, pabuf);
if (glen != len) {
prom_printf("\n%s: 0x%x,%s: expected len %d, got %d\n",
str, mem_node, mem_prop, len, glen);
return (ERR);
}
bzero(&pabuf[len], space - len);
if (verbose)
prom_printf("\nphysavail list:\n");
cb_physavail = (arange_t *)pabuf;
arp = cb_physavail + scnt - 1;
pap = (pphav_t *)cb_physavail + scnt - 1;
for (; scnt--; pap--, arp--) {
pages = mmu_btop(pap->size);
arp->low = ADDR_TO_PN(pap->base);
arp->high = arp->low + pages - 1;
if (verbose) {
prom_printf(" %d: (0x%lx - 0x%lx),\tpages %ld\n",
(int)(arp - cb_physavail),
arp->low, arp->high, (arp->high - arp->low + 1));
}
}
return (0);
}
static int
move_page(caddr_t vaddr, pfn_t oldppn)
{
physaddr_t oldphys, newphys;
pfn_t newppn;
int err;
newppn = find_apage();
newphys = PN_TO_ADDR(newppn);
oldphys = PN_TO_ADDR(oldppn);
CB_VPRINTF((" remap vaddr 0x%p, old 0x%lx/0x%llx,"
" new 0x%lx/0x%llx\n",
(void *)vaddr, oldppn, oldphys, newppn, newphys));
phys_xcopy(oldphys, newphys, MMU_PAGESIZE);
err = prom_remap(MMU_PAGESIZE, vaddr, newphys);
if (err)
prom_printf("\nmove_page: remap error\n");
return (err);
}
int
cb_relocate(void)
{
int is_ostk, is_clash, clash_cnt, ok_cnt;
char *str, *desc, *skip_fmt;
caddr_t ostk_low, ostk_high;
caddr_t virt, saddr, eaddr;
pfn_t ppn;
str = "cb_relocate";
CB_VPRINTF((ent_fmt, str, entry));
ostk_low = (caddr_t)&estack - CB_STACK_SIZE;
ostk_high = (caddr_t)&estack - MMU_PAGESIZE;
saddr = (caddr_t)_start;
eaddr = (caddr_t)PAGE_ROUNDUP((uintptr_t)_end);
install_remap();
skip_fmt = " skip vaddr 0x%p, clash=%d, %s\n";
clash_cnt = ok_cnt = 0;
ppn = cpr_vatopfn(saddr);
for (virt = saddr; virt < eaddr; virt += MMU_PAGESIZE, ppn++) {
is_clash = (cb_bitop(ppn, CB_ISSET) != 0);
if (is_clash)
clash_cnt++;
else
ok_cnt++;
is_ostk = (virt >= ostk_low && virt <= ostk_high);
if (is_ostk)
desc = "orig stack";
else
desc = "text/data";
if (is_ostk) {
CB_VPRINTF((skip_fmt, virt, is_clash, desc));
} else if (is_clash == 0) {
CB_VPRINTF((skip_fmt, virt, is_clash, desc));
(void) cb_bitop(ppn, CB_SETBIT);
} else if (move_page(virt, ppn))
return (ERR);
}
CB_VPRINTF(("%s: total %d, clash %d, ok %d\n",
str, clash_cnt + ok_cnt, clash_cnt, ok_cnt));
ppn = cpr_vatopfn(ostk_low);
prom_free_phys(CB_STACK_SIZE, PN_TO_ADDR(ppn));
CB_VPRINTF(("%s: free old stack (0x%lx - 0x%lx)\n",
str, ppn, ppn + mmu_btop(CB_STACK_SIZE) - 1));
return (0);
}