/* root.c: ROOT IMPLEMENTATION
*
* $Id: //info.ravenbrook.com/project/mps/version/1.110/code/root.c#2 $
* Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
*
* .purpose: This is the implementation of the root datatype.
*
* .design: For design, see <design/root/> and
* design.mps.root-interface. */
#include "mpm.h"
SRCID(root, "$Id: //info.ravenbrook.com/project/mps/version/1.110/code/root.c#2 $");
/* RootStruct -- tracing root structure */
#define RootSig ((Sig)0x51960029) /* SIGnature ROOT */
typedef struct RootStruct {
Sig sig;
Serial serial; /* from arena->rootSerial */
Arena arena; /* owning arena */
RingStruct arenaRing; /* attachment to arena */
Rank rank; /* rank of references in this root */
TraceSet grey; /* traces for which root is grey */
RefSet summary; /* summary of references in root */
RootMode mode; /* mode */
Bool protectable; /* Can protect root? */
Addr protBase; /* base of protectable area */
Addr protLimit; /* limit of protectable area */
AccessSet pm; /* Protection Mode */
RootVar var; /* union discriminator */
union RootUnion {
struct {
mps_root_scan_t scan; /* the function which does the scanning */
void *p; /* environment for scan */
size_t s; /* environment for scan */
} fun;
struct {
Addr *base; /* beginning of table */
Addr *limit; /* one off end of table */
} table;
struct {
Addr *base; /* beginning of table */
Addr *limit; /* one off end of table */
Word mask; /* tag mask for scanning */
} tableMasked;
struct {
mps_reg_scan_t scan; /* function for scanning registers */
Thread thread; /* passed to scan */
void *p; /* passed to scan */
size_t s; /* passed to scan */
} reg;
struct {
mps_fmt_scan_t scan; /* format-like scanner */
Addr base, limit; /* passed to scan */
} fmt;
} the;
} RootStruct;
/* RootVarCheck -- check a Root union discriminator
*
* .rootvarcheck: Synchronize with <code/mpmtypes.h#rootvar> */
Bool RootVarCheck(RootVar rootVar)
{
CHECKL(rootVar == RootTABLE || rootVar == RootTABLE_MASKED
|| rootVar == RootFUN || rootVar == RootFMT || rootVar == RootREG);
UNUSED(rootVar);
return TRUE;
}
/* RootModeCheck */
Bool RootModeCheck(RootMode mode)
{
CHECKL((mode & (RootModeCONSTANT | RootModePROTECTABLE
| RootModePROTECTABLE_INNER))
== mode);
/* RootModePROTECTABLE_INNER implies RootModePROTECTABLE */
CHECKL((mode & RootModePROTECTABLE_INNER) == 0
|| (mode & RootModePROTECTABLE));
UNUSED(mode);
return TRUE;
}
/* RootCheck -- check the consistency of a root structure
*
* .rootcheck: Keep synchonized with <code/mpmst.h#root>. */
Bool RootCheck(Root root)
{
CHECKS(Root, root);
CHECKU(Arena, root->arena);
CHECKL(root->serial < ArenaGlobals(root->arena)->rootSerial);
CHECKL(RingCheck(&root->arenaRing));
CHECKL(RankCheck(root->rank));
CHECKL(TraceSetCheck(root->grey));
/* Don't need to check var here, because of the switch below */
switch(root->var)
{
case RootTABLE:
CHECKL(root->the.table.base != 0);
CHECKL(root->the.table.base < root->the.table.limit);
break;
case RootTABLE_MASKED:
CHECKL(root->the.tableMasked.base != 0);
CHECKL(root->the.tableMasked.base < root->the.tableMasked.limit);
/* Can't check anything about the mask. */
break;
case RootFUN:
CHECKL(root->the.fun.scan != NULL);
break;
case RootREG:
CHECKL(root->the.reg.scan != NULL);
CHECKL(ThreadCheck(root->the.reg.thread));
break;
case RootFMT:
CHECKL(root->the.fmt.scan != NULL);
CHECKL(root->the.fmt.base != 0);
CHECKL(root->the.fmt.base < root->the.fmt.limit);
break;
default:
NOTREACHED;
}
CHECKL(RootModeCheck(root->mode));
CHECKL(BoolCheck(root->protectable));
if (root->protectable) {
CHECKL(root->protBase != (Addr)0);
CHECKL(root->protLimit != (Addr)0);
CHECKL(root->protBase < root->protLimit);
/* there is no AccessSetCheck */
} else {
CHECKL(root->protBase == (Addr)0);
CHECKL(root->protLimit == (Addr)0);
CHECKL(root->pm == (AccessSet)0);
}
return TRUE;
}
/* rootCreate, RootCreateTable, RootCreateReg, RootCreateFmt, RootCreateFun
*
* RootCreate* set up the appropriate union member, and call the generic
* create function to do the actual creation
*
* See <design/root/#init> for initial value. */
static Res rootCreate(Root *rootReturn, Arena arena,
Rank rank, RootMode mode, RootVar type,
union RootUnion *theUnionP)
{
Root root;
Res res;
void *p;
Globals globals;
AVER(rootReturn != NULL);
AVERT(Arena, arena);
AVERT(Rank, rank);
AVERT(RootVar, type);
globals = ArenaGlobals(arena);
res = ControlAlloc(&p, arena, sizeof(RootStruct), FALSE);
if (res != ResOK)
return res;
root = (Root)p; /* Avoid pun */
root->arena = arena;
root->rank = rank;
root->var = type;
root->the = *theUnionP;
root->grey = TraceSetEMPTY;
root->summary = RefSetUNIV;
root->mode = mode;
root->pm = AccessSetEMPTY;
root->protectable = FALSE;
root->protBase = (Addr)0;
root->protLimit = (Addr)0;
/* See <design/arena/#root-ring> */
RingInit(&root->arenaRing);
root->serial = globals->rootSerial;
++globals->rootSerial;
root->sig = RootSig;
AVERT(Root, root);
RingAppend(&globals->rootRing, &root->arenaRing);
*rootReturn = root;
return ResOK;
}
static Res rootCreateProtectable(Root *rootReturn, Arena arena,
Rank rank, RootMode mode, RootVar var,
Addr base, Addr limit,
union RootUnion *theUnion)
{
Res res;
Root root;
Ring node, next;
res = rootCreate(&root, arena, rank, mode, var, theUnion);
if (res != ResOK)
return res;
if (mode & RootModePROTECTABLE) {
root->protectable = TRUE;
if (mode & RootModePROTECTABLE_INNER) {
root->protBase = AddrAlignUp(base, ArenaAlign(arena));
root->protLimit = AddrAlignDown(limit, ArenaAlign(arena));
if (!(root->protBase < root->protLimit)) {
/* root had no inner pages */
root->protectable = FALSE;
root->mode &=~ (RootModePROTECTABLE|RootModePROTECTABLE_INNER);
}
} else {
root->protBase = AddrAlignDown(base, ArenaAlign(arena));
root->protLimit = AddrAlignUp(limit, ArenaAlign(arena));
}
}
/* Check that this root doesn't intersect with any other root */
RING_FOR(node, &ArenaGlobals(arena)->rootRing, next) {
Root trial = RING_ELT(Root, arenaRing, node);
if (trial != root) {
/* (trial->protLimit <= root->protBase */
/* || root->protLimit <= trial->protBase) */
/* is the "okay" state. The negation of this is: */
if (root->protBase < trial->protLimit
&& trial->protBase < root->protLimit) {
NOTREACHED;
RootDestroy(root);
return ResFAIL;
}
}
}
AVERT(Root, root);
*rootReturn = root;
return ResOK;
}
Res RootCreateTable(Root *rootReturn, Arena arena,
Rank rank, RootMode mode, Addr *base, Addr *limit)
{
Res res;
union RootUnion theUnion;
AVER(rootReturn != NULL);
AVERT(Arena, arena);
AVER(RankCheck(rank));
AVER(base != 0);
AVER(base < limit);
theUnion.table.base = base;
theUnion.table.limit = limit;
res = rootCreateProtectable(rootReturn, arena, rank, mode,
RootTABLE, (Addr)base, (Addr)limit, &theUnion);
return res;
}
Res RootCreateTableMasked(Root *rootReturn, Arena arena,
Rank rank, RootMode mode, Addr *base, Addr *limit,
Word mask)
{
union RootUnion theUnion;
AVER(rootReturn != NULL);
AVERT(Arena, arena);
AVER(RankCheck(rank));
AVER(base != 0);
AVER(base < limit);
/* Can't check anything about mask. */
theUnion.tableMasked.base = base;
theUnion.tableMasked.limit = limit;
theUnion.tableMasked.mask = mask;
return rootCreateProtectable(rootReturn, arena, rank, mode, RootTABLE_MASKED,
(Addr)base, (Addr)limit, &theUnion);
}
Res RootCreateReg(Root *rootReturn, Arena arena,
Rank rank, Thread thread,
mps_reg_scan_t scan, void *p, size_t s)
{
union RootUnion theUnion;
AVER(rootReturn != NULL);
AVERT(Arena, arena);
AVER(RankCheck(rank));
AVERT(Thread, thread);
AVER(scan != NULL);
theUnion.reg.scan = scan;
theUnion.reg.thread = thread;
theUnion.reg.p = p;
theUnion.reg.s = s;
return rootCreate(rootReturn, arena, rank, (RootMode)0, RootREG, &theUnion);
}
Res RootCreateFmt(Root *rootReturn, Arena arena,
Rank rank, RootMode mode, mps_fmt_scan_t scan,
Addr base, Addr limit)
{
union RootUnion theUnion;
AVER(rootReturn != NULL);
AVERT(Arena, arena);
AVER(RankCheck(rank));
AVER(FUNCHECK(scan));
AVER(base != 0);
AVER(base < limit);
theUnion.fmt.scan = scan;
theUnion.fmt.base = base;
theUnion.fmt.limit = limit;
return rootCreateProtectable(rootReturn, arena, rank, mode,
RootFMT, base, limit, &theUnion);
}
Res RootCreateFun(Root *rootReturn, Arena arena, Rank rank,
mps_root_scan_t scan, void *p, size_t s)
{
union RootUnion theUnion;
AVER(rootReturn != NULL);
AVERT(Arena, arena);
AVER(RankCheck(rank));
AVER(FUNCHECK(scan));
theUnion.fun.scan = scan;
theUnion.fun.p = p;
theUnion.fun.s = s;
return rootCreate(rootReturn, arena, rank, (RootMode)0, RootFUN, &theUnion);
}
/* RootDestroy -- destroy a root */
void RootDestroy(Root root)
{
Arena arena;
AVERT(Root, root);
arena = RootArena(root);
AVERT(Arena, arena);
RingRemove(&root->arenaRing);
RingFinish(&root->arenaRing);
root->sig = SigInvalid;
ControlFree(arena, root, sizeof(RootStruct));
}
/* RootArena -- return the rank of a root
*
* Must be thread-safe. */
Arena RootArena(Root root)
{
AVER(TESTT(Root, root));
return root->arena;
}
/* RootRank -- return the rank of a root */
Rank RootRank(Root root)
{
AVERT(Root, root);
return root->rank;
}
/* RootPM -- return the protection mode of a root */
AccessSet RootPM(Root root)
{
AVERT(Root, root);
return root->pm;
}
/* RootSummary -- return the summary of a root */
RefSet RootSummary(Root root)
{
AVERT(Root, root);
return root->summary;
}
/* RootGrey -- mark root grey */
void RootGrey(Root root, Trace trace)
{
AVERT(Root, root);
AVERT(Trace, trace);
root->grey = TraceSetAdd(root->grey, trace);
}
static void rootSetSummary(Root root, RefSet summary)
{
AVERT(Root, root);
/* Can't check summary */
if (root->protectable) {
if (summary == RefSetUNIV) {
root->summary = summary;
root->pm &= ~AccessWRITE;
} else {
root->pm |= AccessWRITE;
root->summary = summary;
}
} else
AVER(root->summary == RefSetUNIV);
}
/* RootScan -- scan root */
Res RootScan(ScanState ss, Root root)
{
Res res;
AVERT(Root, root);
AVERT(ScanState, ss);
AVER(root->rank == ss->rank);
if (TraceSetInter(root->grey, ss->traces) == TraceSetEMPTY)
return ResOK;
AVER(ScanStateSummary(ss) == RefSetEMPTY);
if (root->pm != AccessSetEMPTY) {
ProtSet(root->protBase, root->protLimit, AccessSetEMPTY);
}
switch(root->var) {
case RootTABLE:
res = TraceScanArea(ss, root->the.table.base, root->the.table.limit);
ss->scannedSize += AddrOffset(root->the.table.base, root->the.table.limit);
if (res != ResOK)
goto failScan;
break;
case RootTABLE_MASKED:
res = TraceScanAreaMasked(ss,
root->the.tableMasked.base,
root->the.tableMasked.limit,
root->the.tableMasked.mask);
ss->scannedSize += AddrOffset(root->the.table.base, root->the.table.limit);
if (res != ResOK)
goto failScan;
break;
case RootFUN:
res = (*root->the.fun.scan)(&ss->ss_s, root->the.fun.p, root->the.fun.s);
if (res != ResOK)
goto failScan;
break;
case RootREG:
res = (*root->the.reg.scan)(&ss->ss_s, root->the.reg.thread,
root->the.reg.p, root->the.reg.s);
if (res != ResOK)
goto failScan;
break;
case RootFMT:
res = (*root->the.fmt.scan)(&ss->ss_s, root->the.fmt.base, root->the.fmt.limit);
ss->scannedSize += AddrOffset(root->the.fmt.base, root->the.fmt.limit);
if (res != ResOK)
goto failScan;
break;
default:
NOTREACHED;
res = ResUNIMPL;
goto failScan;
}
AVER(res == ResOK);
root->grey = TraceSetDiff(root->grey, ss->traces);
rootSetSummary(root, ScanStateSummary(ss));
EVENT3(RootScan, root, ss->traces, ScanStateSummary(ss));
failScan:
if (root->pm != AccessSetEMPTY) {
ProtSet(root->protBase, root->protLimit, root->pm);
}
return res;
}
/* RootOfAddr -- return the root at addr
*
* Returns TRUE if the addr is in a root (and returns the root in
* *rootReturn) otherwise returns FALSE. Cf. SegOfAddr. */
Bool RootOfAddr(Root *rootReturn, Arena arena, Addr addr)
{
Ring node, next;
AVER(rootReturn != NULL);
AVERT(Arena, arena);
/* addr is arbitrary and can't be checked */
RING_FOR(node, &ArenaGlobals(arena)->rootRing, next) {
Root root = RING_ELT(Root, arenaRing, node);
if (root->protectable && root->protBase <= addr && addr < root->protLimit) {
*rootReturn = root;
return TRUE;
}
}
return FALSE;
}
/* RootAccess -- handle barrier hit on root */
void RootAccess(Root root, AccessSet mode)
{
AVERT(Root, root);
/* Can't AVERT mode. */
AVER((root->pm & mode) != AccessSetEMPTY);
AVER(mode == AccessWRITE); /* only write protection supported */
rootSetSummary(root, RefSetUNIV);
/* Access must now be allowed. */
AVER((root->pm & mode) == AccessSetEMPTY);
ProtSet(root->protBase, root->protLimit, root->pm);
}
/* RootsIterate -- iterate over all the roots in the arena */
Res RootsIterate(Globals arena, RootIterateFn f, void *p)
{
Res res = ResOK;
Ring node, next;
RING_FOR(node, &arena->rootRing, next) {
Root root = RING_ELT(Root, arenaRing, node);
res = (*f)(root, p);
if (res != ResOK)
return res;
}
return res;
}
/* RootDescribe -- describe a root */
Res RootDescribe(Root root, mps_lib_FILE *stream)
{
Res res;
if (!TESTT(Root, root)) return ResFAIL;
if (stream == NULL) return ResFAIL;
res = WriteF(stream,
"Root $P ($U) {\n", (WriteFP)root, (WriteFU)root->serial,
" arena $P ($U)\n", (WriteFP)root->arena,
(WriteFU)root->arena->serial,
" rank $U\n", (WriteFU)root->rank,
" grey $B\n", (WriteFB)root->grey,
" summary $B\n", (WriteFB)root->summary,
NULL);
if (res != ResOK) return res;
switch(root->var) {
case RootTABLE:
res = WriteF(stream,
" table base $A limit $A\n",
root->the.table.base, root->the.table.limit,
NULL);
if (res != ResOK) return res;
break;
case RootTABLE_MASKED:
res = WriteF(stream, " table base $A limit $A mask $B\n",
root->the.tableMasked.base, root->the.tableMasked.limit,
root->the.tableMasked.mask,
NULL);
if (res != ResOK) return res;
break;
case RootFUN:
res = WriteF(stream,
" scan function $F\n", (WriteFF)root->the.fun.scan,
" environment p $P s $W\n",
root->the.fun.p, (WriteFW)root->the.fun.s,
NULL);
if (res != ResOK) return res;
break;
case RootREG:
res = WriteF(stream,
" thread $P\n", (WriteFP)root->the.reg.thread,
" environment p $P", root->the.reg.p,
NULL);
if (res != ResOK) return res;
break;
case RootFMT:
res = WriteF(stream,
" scan function $F\n", (WriteFF)root->the.fmt.scan,
" format base $A limit $A\n",
root->the.fmt.base, root->the.fmt.limit,
NULL);
if (res != ResOK) return res;
break;
default:
NOTREACHED;
}
res = WriteF(stream,
"} Root $P ($U)\n", (WriteFP)root, (WriteFU)root->serial,
NULL);
if (res != ResOK) return res;
return ResOK;
}
/* RootsDescribe -- describe all roots */
Res RootsDescribe(Globals arenaGlobals, mps_lib_FILE *stream)
{
Res res = ResOK;
Ring node, next;
RING_FOR(node, &arenaGlobals->rootRing, next) {
Root root = RING_ELT(Root, arenaRing, node);
res = RootDescribe(root, stream); /* this outputs too much */
if (res != ResOK) return res;
}
return res;
}
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2001-2002 Ravenbrook Limited <http://www.ravenbrook.com/>.
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Redistributions in any form must be accompanied by information on how
* to obtain complete source code for this software and any accompanying
* software that uses this software. The source code must either be
* included in the distribution or be available for no more than the cost
* of distribution plus a nominal fee, and must be freely redistributable
* under reasonable conditions. For an executable file, complete source
* code means the source code for all modules it contains. It does not
* include source code for modules or files that typically accompany the
* major components of the operating system on which the executable file
* runs.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/