1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-03-26 08:41:47 -07:00

Freebsd support.

Copied from Perforce
 Change: 23681
 ServerID: perforce.ravenbrook.com
This commit is contained in:
Nick Barnes 2001-10-31 15:01:00 +00:00
commit 4be3e43fe9
15 changed files with 1323 additions and 108 deletions

View file

@ -19,7 +19,7 @@
#include "mpsw3.h"
#endif
#include <string.h>
#ifdef MPS_OS_LI
#if defined(MPS_OS_LI) || defined(MPS_OS_FR)
#include <pthread.h>
#endif
@ -274,7 +274,7 @@ static void *setup(void *v, size_t s)
"Weak AP Create\n");
die(mps_ap_create(&bogusap, tablepool, MPS_RANK_EXACT),
"Bogus AP Create\n");
test(leafap, exactap, weakap, bogusap);
mps_ap_destroy(bogusap);

View file

@ -125,12 +125,12 @@ static char *parseArgs(int argc, char *argv[])
{
char *name = "mpsio.log";
int i = 1;
if (argc >= 1)
prog = argv[0];
else
prog = "unknown";
while (i < argc) { /* consider argument i */
if (argv[i][0] == '-') { /* it's an option argument */
switch (argv[i][1]) {
@ -138,9 +138,9 @@ static char *parseArgs(int argc, char *argv[])
++ i;
if (i == argc)
usageError();
else
else
name = argv[i];
break;
break;
case 'p': /* partial log */
partialLog = TRUE;
break;
@ -154,7 +154,7 @@ static char *parseArgs(int argc, char *argv[])
usageError();
else
parseEventSpec(argv[i]);
} break;
} break;
case 'b': { /* bucket size */
++ i;
if (i == argc)
@ -165,7 +165,7 @@ static char *parseArgs(int argc, char *argv[])
n = sscanf(argv[i], "%lu", &bucketSize);
if (n != 1) usageError();
}
} break;
} break;
case 'S': /* style */
style = argv[i][2]; /* '\0' for human-readable, 'L' for Lisp, */
break; /* 'C' for CDF. */
@ -192,7 +192,7 @@ static void processEvent(EventProc proc, Event event, Word etime)
if (res != ResOK)
error("Can't record event: error %d.", res);
switch(event->any.code) {
default:
default:
break;
}
}
@ -246,7 +246,7 @@ static void reportEventResults(eventCountArray eventCounts)
{
EventCode i;
unsigned long total = 0;
for(i = 0; i <= EventCodeMAX; ++i) {
total += eventCounts[i];
if (eventEnabled[i])
@ -441,7 +441,7 @@ static void readLog(EventProc proc)
printf("%u", (unsigned)code);
break;
}
switch (style) {
case '\0':
printf(" %8lu", (ulong)eventTime); break;
@ -591,11 +591,16 @@ int main(int argc, char *argv[])
EventProc proc;
Res res;
#if !defined(MPS_OS_FR)
/* GCC -ansi -pedantic -Werror on FreeBSD will fail here
* with the warning "statement with no effect". */
assert(CHECKCONV(ulong, Word));
assert(CHECKCONV(ulong, Addr));
assert(CHECKCONV(ulong, void *));
assert(CHECKCONV(unsigned, EventCode));
assert(CHECKCONV(Addr, void *)); /* for labelled pointers */
#endif
filename = parseArgs(argc, argv);

View file

@ -19,7 +19,6 @@
#include <stdlib.h> /* size_t */
#include <string.h> /* strcmp */
struct EventProcStruct {
Bool partialLog; /* Is this a partial log? */
EventProcReader reader; /* reader fn */
@ -91,7 +90,7 @@ static eventRecord eventTypes[] = {
static size_t eventType2Index(EventType type)
{
size_t i;
for(i = 0; i < eventTypeCount; ++i)
if (eventTypes[i].type == type)
return i;
@ -105,7 +104,7 @@ static size_t eventType2Index(EventType type)
static size_t eventCode2Index(EventCode code, Bool errorp)
{
size_t i;
for(i = 0; i < eventTypeCount; ++i)
if (eventTypes[i].code == code)
return i;
@ -120,7 +119,7 @@ static size_t eventCode2Index(EventCode code, Bool errorp)
EventCode EventName2Code(char *name)
{
size_t i;
for(i = 0; i < eventTypeCount; ++i)
if (strcmp(eventTypes[i].name, name) == 0) {
assert(eventTypes[i].code <= EventCodeMAX);
@ -250,7 +249,7 @@ EventString LabelText(EventProc proc, Word id)
Res EventRead(Event *eventReturn, EventProc proc)
{
size_t index, length;
size_t eventIndex, length;
Res res;
EventType type;
Event event;
@ -259,8 +258,8 @@ Res EventRead(Event *eventReturn, EventProc proc)
res = proc->reader(proc->readerP, &type, sizeof(EventType));
if (res != ResOK) return res;
index = eventType2Index(type);
length = eventTypes[index].length;
eventIndex = eventType2Index(type);
length = eventTypes[eventIndex].length;
if (proc->cachedEvent != NULL) {
event = proc->cachedEvent;
proc->cachedEvent = NULL;
@ -306,7 +305,7 @@ Res EventRecord(EventProc proc, Event event, Word etime)
switch(event->any.code) {
case EventIntern: { /* id, label */
Symbol sym = malloc(sizeof(symbolStruct));
if (sym == NULL) return ResMEMORY;
sym->id = event->ws.w0;
res = eventStringCopy(&(sym->name), &(event->ws.s1));
@ -332,7 +331,7 @@ Res EventRecord(EventProc proc, Event event, Word etime)
else
res = TableDefine(proc->labelTable, (Word)label->addr, label);
} break;
default:
default:
res = ResOK;
break;
}
@ -386,7 +385,11 @@ Res EventProcCreate(EventProc *procReturn, Bool partial,
assert(CHECKFIELD(EventUnion, any.code, EventWSStruct, code));
assert(CHECKFIELD(EventUnion, any.clock, EventWSStruct, clock));
/* check use of labelTable */
#if !defined(MPS_OS_FR)
/* GCC -ansi -pedantic -Werror on FreeBSD will fail here
* with the warning "statement with no effect". */
assert(sizeof(Word) >= sizeof(Addr));
#endif
proc->partialLog = partial;
proc->reader = reader; proc->readerP = readerP;

25
mps/code/fri4gc.gmk Normal file
View file

@ -0,0 +1,25 @@
# impl.gmk.fri4gc: BUILD FOR FreeBSD/INTEL/GCC PLATFORM
#
# $HopeName: MMsrc!lii4gc.gmk(trunk.5) $
# Copyright (C) 2000 Harlequin Limited. All rights reserved.
PFM = fri4gc
PFMDEFS = -D_REENTRANT
MPMPF = mpsliban.c mpsioan.c lockfr.c thfri4.c pthrdext.c vmfr.c \
protfr.c protfri3.c prmcan.c ssfri3.c span.c
SWPF = than.c vmfr.c protsw.c prmcan.c ssan.c
LIBS = -lm -pthread
include gc.gmk
CC = cc
# Suppress some warnings (SuSE).
# .void: -Wpointer-arith cannot be used because the string.h header does
# arithmetic on void*.
CFLAGSCOMPILER := $(subst -Wpointer-arith,,$(CFLAGSCOMPILER))
include comm.gmk

236
mps/code/lockfr.c Normal file
View file

@ -0,0 +1,236 @@
/* impl.c.lockfr: RECURSIVE LOCKS FOR POSIX SYSTEMS
*
* $HopeName$
* Copyright (C) 2000 Harlequin Limited. All rights reserved.
*
* .freebsd: This implementation supports FreeBSD (platform
* MPS_OS_FR).
*
* .posix: In fact, the implementation should be reusable for most POSIX
* implementations, but may need some customization for each.
*
* .design: These locks are implemented using mutexes.
*
* .recursive: Mutexes support both non-recursive and recursive
* locking, but only at initialization time. This doesn't match the
* API of MPS Lock module, which chooses at locking time, so all locks
* are made (non-recursive) errorchecking. Recursive locks are
* implemented by checking the error code.
*
* .claims: During use the claims field is updated to remember the
* number of claims acquired on a lock. This field must only be
* modified while we hold the mutex. */
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#include "mpmtypes.h"
#include "lock.h"
#include "config.h"
#ifndef MPS_OS_FR
#error "lockfr.c is FreeBSD specific but MPS_OS_FR not defined"
#endif
SRCID(lockfr, "$HopeName$");
/* LockStruct -- the MPS lock structure
*
* .lock.posix: Posix lock structure; uses a mutex.
*/
typedef struct LockStruct {
Sig sig; /* design.mps.sig */
unsigned long claims; /* # claims held by owner */
pthread_mutex_t mut; /* the mutex itself */
} LockStruct;
/* LockSize -- size of a LockStruct */
size_t LockSize(void)
{
return sizeof(LockStruct);
}
/* LockCheck -- check a lock */
Bool LockCheck(Lock lock)
{
CHECKS(Lock, lock);
/* While claims can't be very large, I don't dare to put a limit on it. */
/* There's no way to test the mutex, or check if it's held by somebody. */
return TRUE;
}
/* LockInit -- initialize a lock */
void LockInit(Lock lock)
{
pthread_mutexattr_t attr;
int res;
AVER(lock != NULL);
lock->claims = 0;
res = pthread_mutexattr_init(&attr);
AVER(res == 0);
res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
AVER(res == 0);
res = pthread_mutex_init(&lock->mut, &attr);
AVER(res == 0);
res = pthread_mutexattr_destroy(&attr);
AVER(res == 0);
lock->sig = LockSig;
AVERT(Lock, lock);
}
/* LockFinish -- finish a lock */
void LockFinish(Lock lock)
{
int res;
AVERT(Lock, lock);
/* Lock should not be finished while held */
AVER(lock->claims == 0);
res = pthread_mutex_destroy(&lock->mut);
AVER(res == 0);
lock->sig = SigInvalid;
}
/* LockClaim -- claim a lock (non-recursive) */
void LockClaim(Lock lock)
{
int res;
AVERT(Lock, lock);
res = pthread_mutex_lock(&lock->mut);
/* pthread_mutex_lock will error if we own the lock already. */
AVER(res == 0);
/* This should be the first claim. Now we own the mutex */
/* it is ok to check this. */
AVER(lock->claims == 0);
lock->claims = 1;
}
/* LockReleaseMPM -- release a lock (non-recursive) */
void LockReleaseMPM(Lock lock)
{
int res;
AVERT(Lock, lock);
AVER(lock->claims == 1); /* The lock should only be held once */
lock->claims = 0; /* Must set this before releasing the lock */
res = pthread_mutex_unlock(&lock->mut);
/* pthread_mutex_unlock will error if we didn't own the lock. */
AVER(res == 0);
}
/* LockClaimRecursive -- claim a lock (recursive) */
void LockClaimRecursive(Lock lock)
{
int res;
AVERT(Lock, lock);
res = pthread_mutex_lock(&lock->mut);
/* pthread_mutex_lock will return: */
/* 0 if we have just claimed the lock */
/* EDEADLK if we own the lock already. */
AVER((res == 0 && lock->claims == 0) ||
(res == EDEADLK && lock->claims > 0));
++lock->claims;
AVER(lock->claims > 0);
}
/* LockReleaseRecursive -- release a lock (recursive) */
void LockReleaseRecursive(Lock lock)
{
int res;
AVERT(Lock, lock);
AVER(lock->claims > 0);
--lock->claims;
if (lock->claims == 0) {
res = pthread_mutex_unlock(&lock->mut);
/* pthread_mutex_unlock will error if we didn't own the lock. */
AVER(res == 0);
}
}
/* Global locks
*
* .global: The two "global" locks are statically allocated normal locks.
*/
static LockStruct globalLockStruct;
static LockStruct globalRecLockStruct;
static Lock globalLock = &globalLockStruct;
static Lock globalRecLock = &globalRecLockStruct;
static pthread_once_t isGlobalLockInit = PTHREAD_ONCE_INIT;
static void globalLockInit(void)
{
LockInit(globalLock);
LockInit(globalRecLock);
}
/* LockClaimGlobalRecursive -- claim the global recursive lock */
void LockClaimGlobalRecursive(void)
{
int res;
/* Ensure the global lock has been initialized */
res = pthread_once(&isGlobalLockInit, globalLockInit);
AVER(res == 0);
LockClaimRecursive(globalRecLock);
}
/* LockReleaseGlobalRecursive -- release the global recursive lock */
void LockReleaseGlobalRecursive(void)
{
LockReleaseRecursive(globalRecLock);
}
/* LockClaimGlobal -- claim the global non-recursive lock */
void LockClaimGlobal(void)
{
int res;
/* Ensure the global lock has been initialized */
res = pthread_once(&isGlobalLockInit, globalLockInit);
AVER(res == 0);
LockClaim(globalLock);
}
/* LockReleaseGlobal -- release the global non-recursive lock */
void LockReleaseGlobal(void)
{
LockReleaseMPM(globalLock);
}

View file

@ -160,7 +160,7 @@
/* GCC 2.7.2.1, gcc -E -dM -traditional-cpp and <URL:http://developer.apple.c
* om/techpubs/macosx/System/Documentation/Developer/YellowBox/Reference/DevT
* ools/Preprocessor/Preprocessor.[ef].html>
* ools/Preprocessor/Preprocessor.[ef].html>
*/
#elif defined(__APPLE__) && defined(__ppc__) && defined(__MACH__) && defined(__GNUC__)
@ -291,6 +291,20 @@
#define MPS_WORD_SHIFT 5
#define MPS_PF_ALIGN 8 /* @@@@ not tested */
/* GCC 2.95.3, gcc -E -dM
*/
#elif defined(__FreeBSD__) && defined (__i386__) && defined (__GNUC__)
#define MPS_PF_FRI4GC
#define MPS_PF_STRING "fri4gc"
#define MPS_OS_FR
#define MPS_ARCH_I4
#define MPS_BUILD_GC
#define MPS_T_WORD unsigned long
#define MPS_WORD_WIDTH 32
#define MPS_WORD_SHIFT 5
#define MPS_PF_ALIGN 4
#else
#error "Unable to detect target platform"
#endif

21
mps/code/prmcfr.h Normal file
View file

@ -0,0 +1,21 @@
/* impl.h.prmcfr: PROTECTION MUTATOR CONTEXT (FREEBSD)
*
* $HopeName: $
* Copyright (C) 1998 The Harlequin Group Limited. All rights reserved.
*
* .readership: MPS developers.
*/
#ifndef prmcfr_h
#define prmcfr_h
#include "mpm.h"
#include <signal.h>
typedef struct MutatorFaultContextStruct { /* Protection fault context data */
ucontext_t *ucontext;
} MutatorFaultContextStruct;
#endif /* prmcfr_h */

93
mps/code/protfr.c Normal file
View file

@ -0,0 +1,93 @@
/* impl.c.protfr: PROTECTION FOR FreeBSD
*
* $HopeName: $
* Copyright (C) 1995,1999 Harlequin Group, all rights reserved
*
*/
#include "mpm.h"
#ifndef MPS_OS_FR
#error "protfr.c is FreeBSD specific, but MPS_OS_FR is not set"
#endif
#ifndef PROTECTION
#error "protfr.c implements protection, but PROTECTION is not set"
#endif
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
SRCID(protfr, "$HopeName: $");
/* ProtSet -- set protection
*
* This is just a thin veneer on top of mprotect(2).
*/
void ProtSet(Addr base, Addr limit, AccessSet mode)
{
int flags;
int res;
AVER(sizeof(int) == sizeof(Addr)); /* should be redundant; will fail on Alpha */
AVER(base < limit);
AVER(base != 0);
AVER(AddrOffset(base, limit) <= INT_MAX); /* should be redundant */
#if 0
/* .flags.trouble: This less strict version of flags (which allows write
* access unless explicitly told not to) caused mmqa test 37 to fail.
* This might be a bug in MPS, so for now we go with the stricter
* version that matches the Win32 implementation. */
/* .flags.trouble.freebsd: the above comment was in the Linux version
* of this code; I haven't verified it for FreeBSD. */
flags = 0;
if((mode & AccessREAD) == 0)
flags |= PROT_READ | PROT_EXEC;
if((mode & AccessWRITE) == 0)
flags |= PROT_WRITE;
#endif
flags = PROT_READ | PROT_WRITE | PROT_EXEC;
if((mode & AccessWRITE) != 0)
flags = PROT_READ | PROT_EXEC;
if((mode & AccessREAD) != 0)
flags = 0;
res = mprotect((void *)base, (size_t)AddrOffset(base, limit), flags);
AVER(res == 0);
}
/* ProtSync -- synchronize protection settings with hardware
*
* This does nothing under FreeBSD.
*/
void ProtSync(Arena arena)
{
NOOP;
}
/* ProtTramp -- protection trampoline
*
* The protection trampoline is trivial under FreeBSD, as there is
* nothing that needs to be done in the dynamic context of the mutator
* in order to catch faults. (Contrast this with Win32 Structured
* Exception Handling.)
*/
void ProtTramp(void **resultReturn, void *(*f)(void *, size_t),
void *p, size_t s)
{
AVER(resultReturn != NULL);
AVER(FUNCHECK(f));
/* Can't check p and s as they are interpreted by the client */
*resultReturn = (*f)(p, s);
}

137
mps/code/protfri3.c Normal file
View file

@ -0,0 +1,137 @@
/* impl.c.protfri3: PROTECTION FOR FREEBSD (INTEL 386)
*
* $HopeName: MMsrc!protlii3.c(trunk.3) $
* Copyright (C) 2000 Harlequin Limited. All rights reserved.
*
* SOURCES
*
* .source.i486: Intel486 Microprocessor Family Programmer's
* Reference Manual
*
* .source.man: sigaction(2): FreeBSD System Calls Manual.
*/
#include "prmcfr.h"
#ifndef MPS_OS_FR
#error "protfri3.c is FreeBSD-specific, but MPS_OS_FR is not set"
#endif
#if !defined(MPS_ARCH_I3) && !defined(MPS_ARCH_I4)
#error "protfri3.c is Intel-specific, but MPS_ARCH_I3 or MPS_ARCH_I4 is not set"
#endif
#ifndef PROTECTION
#error "protfri3.c implements protection, but PROTECTION is not set"
#endif
#include <signal.h>
#include <machine/trap.h>
SRCID(protfri3, "$HopeName: MMsrc!protlii3.c(trunk.3) $");
/* The previously-installed signal action, as returned by */
/* sigaction(3). See ProtSetup. */
static struct sigaction sigNext;
/* sigHandle -- protection signal handler
*
* This is the signal handler installed by ProtSetup to deal with
* protection faults. It is installed on the SIGBUS signal. It
* decodes the protection fault details from the signal context and
* passes them to ArenaAccess, which attempts to handle the fault and
* remove its cause. If the fault is handled, then the handler
* returns and execution resumes. If it isn't handled, then
* sigHandle does its best to pass the signal on to the previously
* installed signal handler (sigNext).
*
* .sigh.args: The sigaction manual page .source.man documents three
* different handler prototypes: ANSI C sa_handler, traditional BSD
* sa_handler, and POSIX SA_SIGINFO sa_sigaction. The ANSI C
* prototype isn't powerful enough for us (can't get addresses), and
* the manual page deprecates the BSD sa_handler in favour of the
* POSIX SA_SIGINFO sa_sigaction. In that prototype, the arguments
* are: signal number, pointer to signal info structure, pointer to
* signal context structure.
*
* .sigh.context: We only know how to handle signals with code
* BUS_PAGE_FAULT, where info->si_addr gives the fault address.
*
* .sigh.mode: The fault type (read/write) does not appear to be
* available to the signal handler (see mail archive).
*/
static void sigHandle(int sig, siginfo_t *info, void *context) /* .sigh.args */
{
AVER(sig == SIGBUS);
if(info->si_code == BUS_PAGE_FAULT) { /* .sigh.context */
AccessSet mode;
Addr base, limit;
mode = AccessREAD | AccessWRITE; /* .sigh.mode */
/* We assume that the access is for one word at the address. */
base = (Addr)info->si_addr; /* .sigh.context */
limit = AddrAdd(base, (Size)sizeof(Addr));
/* Offer each protection structure the opportunity to handle the */
/* exception. If it succeeds, then allow the mutator to continue. */
if(ArenaAccess(base, mode, NULL))
return;
}
/* The exception was not handled by any known protection structure, */
/* so throw it to the previously installed handler. */
/* @@@@ This is really weak. */
/* Need to implement rest of the contract of sigaction */
/* We might also want to set SA_RESETHAND in the flags and explicitly */
/* reinstall the handler from withint itself so the SIG_DFL/SIG_IGN */
/* case can work properly by just returning. */
switch ((int)sigNext.sa_handler) {
case (int)SIG_DFL:
case (int)SIG_IGN:
abort();
NOTREACHED;
break;
default:
if ((int)sigNext.sa_handler & SA_SIGINFO) {
(*sigNext.sa_sigaction)(sig, info, context);
} else {
/* @@@@ what if the previous handler is BSD-style? */
/* We don't have a struct sigcontext to pass to it.
The second argument (the code) is just info->si_code
but the third argument (the sigcontext) we would have to
fake from the ucontext. We could do that. */
(*sigNext.sa_handler)(sig);
}
break;
}
}
/* ProtSetup -- global protection setup
*
* Under FreeBSD, the global setup involves installing a signal
* handler on SIGBUS to catch and handle page faults (see
* sigHandle). The previous handler is recorded so that it can be
* reached from sigHandle if it fails to handle the fault.
*
* NOTE: There are problems with this approach:
* 1. we can't honor the sa_flags for the previous handler,
* 2. what if this thread is suspended just after calling signal(3)?
* The sigNext variable will never be initialized! */
void ProtSetup(void)
{
struct sigaction sa;
int result;
sa.sa_sigaction = sigHandle;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
result = sigaction(SIGBUS, &sa, &sigNext);
AVER(result == 0);
}

View file

@ -13,9 +13,13 @@
*/
#include "mpm.h"
#if defined(MPS_OS_LI)
/* open sesame magic */
#define _BSD_SOURCE 1
#define _POSIX_C_SOURCE 1
#endif
#include <pthread.h>
#include <sched.h>
@ -26,7 +30,6 @@
#include <stdlib.h>
#include "pthrdext.h"
#include "mpm.h"
SRCID(pthreadext, "$HopeName$");
@ -37,7 +40,7 @@ SRCID(pthreadext, "$HopeName$");
*/
#define PTHREADEXT_SIGSUSPEND SIGXFSZ
#define PTHREADEXT_SIGRESUME SIGPWR
#define PTHREADEXT_SIGRESUME SIGXCPU
/* Static data initiatialized on first use of the module
@ -63,9 +66,89 @@ static PThreadext suspendingVictim = NULL; /* current victim */
static RingStruct suspendedRing; /* PThreadext suspend ring */
static void suspendSignalHandler(int sig, struct sigcontext scp);
static void resumeSignalHandler(int sig);
/* suspendSignalHandler -- signal handler called when suspending a thread
*
* See design.mps.pthreadext.impl.suspend-handler
*
* The interface for determining the MFC might be platform specific.
*
* Handle PTHREADEXT_SIGSUSPEND in the target thread, to suspend it until
* receiving PTHREADEXT_SIGRESUME (resume). Note that this is run with both
* PTHREADEXT_SIGSUSPEND and PTHREADEXT_SIGRESUME blocked. Having
* PTHREADEXT_SIGRESUME blocked prevents a resume before we can finish the
* suspend protocol.
*/
#if defined(MPS_OS_LI)
#include "prmcli.h"
static void suspendSignalHandler(int sig, struct sigcontext scp)
{
sigset_t signal_set;
MutatorFaultContextStruct mfContext;
AVER(sig == PTHREADEXT_SIGSUSPEND);
UNUSED(sig);
AVER(suspendingVictim != NULL);
mfContext.scp = &scp;
suspendingVictim->suspendedMFC = &mfContext;
/* Block all signals except PTHREADEXT_SIGRESUME while suspended. */
sigfillset(&signal_set);
sigdelset(&signal_set, PTHREADEXT_SIGRESUME);
sem_post(&pthreadextSem);
sigsuspend(&signal_set);
/* Once here, the resume signal handler has run to completion. */
return;
}
#elif defined(MPS_OS_FR)
#include "prmcfr.h"
static void suspendSignalHandler(int sig,
siginfo_t *info,
void *context)
{
sigset_t signal_set;
ucontext_t ucontext;
MutatorFaultContextStruct mfContext;
AVER(sig == PTHREADEXT_SIGSUSPEND);
UNUSED(sig);
AVER(suspendingVictim != NULL);
/* copy the ucontext structure so we definitely have it on our stack,
* not (e.g.) shared with other threads. */
ucontext = *(ucontext_t *)context;
mfContext.ucontext = &ucontext;
suspendingVictim->suspendedMFC = &mfContext;
/* Block all signals except PTHREADEXT_SIGRESUME while suspended. */
sigfillset(&signal_set);
sigdelset(&signal_set, PTHREADEXT_SIGRESUME);
sem_post(&pthreadextSem);
sigsuspend(&signal_set);
/* Once here, the resume signal handler has run to completion. */
return;
}
#endif
/* resumeSignalHandler -- signal handler called when resuming a thread
*
* See design.mps.pthreadext.impl.suspend-handler
*/
static void resumeSignalHandler(int sig)
{
AVER(sig == PTHREADEXT_SIGRESUME);
UNUSED(sig);
return;
}
/* PThreadextModuleInit -- Initialize the PThreadext module
*
@ -96,13 +179,20 @@ static void PThreadextModuleInit(void)
/* PTHREADEXT_SIGRESUME signal cannot be delivered before the */
/* target thread calls sigsuspend.) */
pthreadext_sigsuspend.sa_flags = 0;
pthreadext_sigsuspend.sa_handler = (__sighandler_t)suspendSignalHandler;
status = sigemptyset(&pthreadext_sigsuspend.sa_mask);
AVER(status == 0);
status = sigaddset(&pthreadext_sigsuspend.sa_mask, PTHREADEXT_SIGRESUME);
AVER(status == 0);
#if defined(MPS_OS_LI)
pthreadext_sigsuspend.sa_flags = 0;
pthreadext_sigsuspend.sa_handler = (__sighandler_t)suspendSignalHandler;
#elif defined(MPS_OS_FR)
pthreadext_sigsuspend.sa_flags = SA_SIGINFO;
pthreadext_sigsuspend.sa_sigaction = suspendSignalHandler;
#endif
pthreadext_sigresume.sa_flags = 0;
pthreadext_sigresume.sa_handler = resumeSignalHandler;
status = sigemptyset(&pthreadext_sigresume.sa_mask);
@ -131,7 +221,7 @@ extern Bool PThreadextCheck(PThreadext pthreadext)
/* can't check ID */
CHECKL(RingCheck(&pthreadext->threadRing));
CHECKL(RingCheck(&pthreadext->idRing));
if (pthreadext->suspendedScp == NULL) {
if (pthreadext->suspendedMFC == NULL) {
/* not suspended */
CHECKL(RingIsSingle(&pthreadext->threadRing));
CHECKL(RingIsSingle(&pthreadext->idRing));
@ -142,7 +232,7 @@ extern Bool PThreadextCheck(PThreadext pthreadext)
RING_FOR(node, &pthreadext->idRing, next) {
PThreadext pt = RING_ELT(PThreadext, idRing, node);
CHECKL(pt->id == pthreadext->id);
CHECKL(pt->suspendedScp == pthreadext->suspendedScp);
CHECKL(pt->suspendedMFC == pthreadext->suspendedMFC);
}
}
status = pthread_mutex_unlock(&pthreadextMut);
@ -163,7 +253,7 @@ extern void PThreadextInit(PThreadext pthreadext, pthread_t id)
AVER(status == 0);
pthreadext->id = id;
pthreadext->suspendedScp = NULL;
pthreadext->suspendedMFC = NULL;
RingInit(&pthreadext->threadRing);
RingInit(&pthreadext->idRing);
pthreadext->sig = PThreadextSig;
@ -185,7 +275,7 @@ extern void PThreadextFinish(PThreadext pthreadext)
status = pthread_mutex_lock(&pthreadextMut);
AVER(status == 0);
if(pthreadext->suspendedScp == NULL) {
if(pthreadext->suspendedMFC == NULL) {
AVER(RingIsSingle(&pthreadext->threadRing));
AVER(RingIsSingle(&pthreadext->idRing));
} else {
@ -205,60 +295,12 @@ extern void PThreadextFinish(PThreadext pthreadext)
}
/* suspendSignalHandler -- signal handler called when suspending a thread
*
* See design.mps.pthreadext.impl.suspend-handler
*
* The interface for determining the sigcontext might be platform specific.
*
* Handle PTHREADEXT_SIGSUSPEND in the target thread, to suspend it until
* receiving PTHREADEXT_SIGRESUME (resume). Note that this is run with both
* PTHREADEXT_SIGSUSPEND and PTHREADEXT_SIGRESUME blocked. Having
* PTHREADEXT_SIGRESUME blocked prevents a resume before we can finish the
* suspend protocol.
*/
static void suspendSignalHandler(int sig, struct sigcontext scp)
{
sigset_t signal_set;
AVER(sig == PTHREADEXT_SIGSUSPEND);
UNUSED(sig);
/* Tell caller about the sigcontext. */
AVER(suspendingVictim != NULL);
suspendingVictim->suspendedScp = &scp;
/* Block all signals except PTHREADEXT_SIGRESUME while suspended. */
sigfillset(&signal_set);
sigdelset(&signal_set, PTHREADEXT_SIGRESUME);
sem_post(&pthreadextSem);
sigsuspend(&signal_set);
/* Once here, the resume signal handler has run to completion. */
return;
}
/* resumeSignalHandler -- signal handler called when resuming a thread
*
* See design.mps.pthreadext.impl.suspend-handler
*/
static void resumeSignalHandler(int sig)
{
AVER(sig == PTHREADEXT_SIGRESUME);
UNUSED(sig);
return;
}
/* PThreadextSuspend -- suspend a thread
*
* See design.mps.pthreadext.impl.suspend
*/
Res PThreadextSuspend(PThreadext target, struct sigcontext **contextReturn)
Res PThreadextSuspend(PThreadext target, MutatorFaultContext *contextReturn)
{
Ring node, next;
Res res;
@ -266,7 +308,7 @@ Res PThreadextSuspend(PThreadext target, struct sigcontext **contextReturn)
AVERT(PThreadext, target);
AVER(contextReturn != NULL);
AVER(target->suspendedScp == NULL); /* multiple suspends illegal */
AVER(target->suspendedMFC == NULL); /* multiple suspends illegal */
/* Serialize access to suspend, makes life easier */
status = pthread_mutex_lock(&pthreadextMut);
@ -280,7 +322,7 @@ Res PThreadextSuspend(PThreadext target, struct sigcontext **contextReturn)
PThreadext alreadySusp = RING_ELT(PThreadext, threadRing, node);
if (alreadySusp->id == target->id) {
RingAppend(&alreadySusp->idRing, &target->idRing);
target->suspendedScp = alreadySusp->suspendedScp;
target->suspendedMFC = alreadySusp->suspendedMFC;
goto noteSuspended;
}
}
@ -302,9 +344,9 @@ Res PThreadextSuspend(PThreadext target, struct sigcontext **contextReturn)
}
noteSuspended:
AVER(target->suspendedScp != NULL);
AVER(target->suspendedMFC != NULL);
RingAppend(&suspendedRing, &target->threadRing);
*contextReturn = target->suspendedScp;
*contextReturn = target->suspendedMFC;
res = ResOK;
unlock:
@ -316,7 +358,7 @@ unlock:
/* PThreadextResume -- resume a suspended thread
*
*
* See design.mps.pthreadext.impl.resume
*/
@ -327,7 +369,7 @@ Res PThreadextResume(PThreadext target)
AVERT(PThreadext, target);
AVER(pthreadextModuleInitialized); /* must have been a prior suspend */
AVER(target->suspendedScp != NULL);
AVER(target->suspendedMFC != NULL);
/* Serialize access to suspend, makes life easier. */
status = pthread_mutex_lock(&pthreadextMut);
@ -353,7 +395,7 @@ Res PThreadextResume(PThreadext target)
noteResumed:
/* Remove the thread from the suspended ring */
RingRemove(&target->threadRing);
target->suspendedScp = NULL;
target->suspendedMFC = NULL;
res = ResOK;
unlock:

View file

@ -26,13 +26,13 @@ typedef struct PThreadextStruct *PThreadext;
/* PThreadextStruct -- structure definition
*
* Should be embedded in a client structure
* Should be embedded in a client structure
*/
typedef struct PThreadextStruct {
Sig sig; /* design.mps.sig */
pthread_t id; /* Thread ID */
struct sigcontext *suspendedScp; /* sigcontext if suspended */
MutatorFaultContext suspendedMFC; /* context if suspended */
RingStruct threadRing; /* ring of suspended threads */
RingStruct idRing; /* duplicate suspensions for id */
} PThreadextStruct;
@ -56,8 +56,8 @@ extern void PThreadextFinish(PThreadext pthreadext);
/* PThreadextSuspend -- Suspend a pthreadext and return its context. */
extern Res PThreadextSuspend(PThreadext pthreadext,
struct sigcontext **contextReturn);
extern Res PThreadextSuspend(PThreadext pthreadext,
MutatorFaultContext *contextReturn);
/* PThreadextResume -- Resume a suspended pthreadext */

58
mps/code/ssfri3.c Normal file
View file

@ -0,0 +1,58 @@
/* impl.c.ssfri3: FREEBSD/INTEL STACK SCANNING
*
* $HopeName: MMsrc!sslii3.c(trunk.1) $
* Copyright (C) 1999. Harlequin Group plc. All rights reserved.
*
* This scans the stack and fixes the registers which may contain
* roots. See design.mps.thread-manager
*
* The registers edi, esi, ebx are the registers defined to be preserved
* across function calls and therefore may contain roots.
* These are pushed on the stack for scanning.
*
* SOURCES
*
* .source.callees.saves: Set of callee-saved registers taken from
* CALL_USED_REGISTERS in <gcc-sources>/config/i386/i386.h.
*
* ASSUMPTIONS
*
* .assume.align: The stack pointer is assumed to be aligned on a word
* boundary.
*
* .assume.asm.stack: The compiler must not do wacky things with the
* stack pointer around a call since we need to ensure that the
* callee-save regs are visible during TraceScanArea.
*
* .assume.asm.order: The volatile modifier should prevent movement
* of code, which might break .assume.asm.stack.
*
*/
#include "mpm.h"
SRCID(ssfri3, "$HopeName: MMsrc!sslii3.c(trunk.1) $");
/* .assume.asm.order */
#define ASMV(x) __asm__ volatile (x)
Res StackScan(ScanState ss, Addr *stackBot)
{
Addr *stackTop;
Res res;
/* .assume.asm.stack */
ASMV("push %ebx"); /* These registers are callee-saved */
ASMV("push %esi"); /* and so may contain roots. They are pushed */
ASMV("push %edi"); /* for scanning. See .source.callees.saves. */
ASMV("mov %%esp, %0" : "=r" (stackTop) :); /* stackTop = esp */
AVER(AddrIsAligned((Addr)stackTop, sizeof(Addr))); /* .assume.align */
res = TraceScanArea(ss, stackTop, stackBot);
ASMV("add $12, %esp"); /* pop 3 regs to restore the stack pointer */
return res;
}

303
mps/code/thfri4.c Normal file
View file

@ -0,0 +1,303 @@
/* impl.c.thfri3: Threads Manager for Intel x86 systems on FreeBSD
*
* $HopeName: MMsrc!thlii4.c(trunk.2) $
* Copyright (C) 2000 Harlequin Limited. All rights reserved.
*
* .purpose: This is a pthreads implementation of the threads manager.
* This implements impl.h.th.
*
* .design: See design.mps.thread-manager.
*
* .thread.id: The thread id is used to identify the current thread.
*
* ASSUMPTIONS
*
* .error.resume: PThreadextResume is assumed to succeed unless the thread
* has been destroyed.
* .error.suspend: PThreadextSuspend is assumed to succeed unless the thread
* has been destroyed. In this case, the suspend context is set to NULL;
*
* .stack.full-descend: assumes full descending stack.
* i.e. stack pointer points to the last allocated location;
* stack grows downwards.
*
* .stack.below-bottom: it's legal for the stack pointer to be at a
* higher address than the registered bottom of stack. This might
* happen if the stack of another thread doesn't contain any frames
* belonging to the client language. In this case, the stack should
* not be scanned.
*
* .stack.align: assume roots on the stack are always word-aligned,
* but don't assume that the stack pointer is necessarily
* word-aligned at the time of reading the context of another thread.
*
* .sp: The stack pointer in the context is ESP.
* .context.regroots: The root regs are EDI, ESI, EBX, EDX, ECX, EAX are
* assumed to be recorded in the context at pointer-aligned boundaries.
*/
#include "prmcfr.h"
#include "mpm.h"
#if !defined(MPS_OS_FR) || !defined(MPS_ARCH_I4)
#error "Compiling thfri4 when MPS_OS_FR or MPS_ARCH_I4 not defined."
#endif
#include <pthread.h>
#include "pthrdext.h"
SRCID(thfri4, "$HopeName: MMsrc!thlii4.c(trunk.2) $");
/* ThreadStruct -- thread desriptor */
typedef struct ThreadStruct { /* PThreads thread structure */
Sig sig; /* design.mps.sig */
Serial serial; /* from arena->threadSerial */
Arena arena; /* owning arena */
RingStruct arenaRing; /* threads attached to arena */
PThreadextStruct thrextStruct; /* PThreads extension */
pthread_t id; /* Pthread object of thread */
MutatorFaultContext mfc; /* Context if thread is suspended */
} ThreadStruct;
/* ThreadCheck -- check a thread */
Bool ThreadCheck(Thread thread)
{
CHECKS(Thread, thread);
CHECKU(Arena, thread->arena);
CHECKL(thread->serial < thread->arena->threadSerial);
CHECKL(RingCheck(&thread->arenaRing));
CHECKD(PThreadext, &thread->thrextStruct);
return TRUE;
}
Bool ThreadCheckSimple(Thread thread)
{
CHECKS(Thread, thread);
return TRUE;
}
/* ThreadRegister -- register a thread with an arena */
Res ThreadRegister(Thread *threadReturn, Arena arena)
{
Res res;
Thread thread;
void *p;
AVER(threadReturn != NULL);
AVERT(Arena, arena);
res = ControlAlloc(&p, arena, sizeof(ThreadStruct),
/* withReservoirPermit */ FALSE);
if(res != ResOK)
return res;
thread = (Thread)p;
thread->id = pthread_self();
RingInit(&thread->arenaRing);
thread->sig = ThreadSig;
thread->serial = arena->threadSerial;
++arena->threadSerial;
thread->arena = arena;
thread->mfc = NULL;
PThreadextInit(&thread->thrextStruct, thread->id);
AVERT(Thread, thread);
RingAppend(ArenaThreadRing(arena), &thread->arenaRing);
*threadReturn = thread;
return ResOK;
}
/* ThreadDeregister -- deregister a thread from an arena */
void ThreadDeregister(Thread thread, Arena arena)
{
AVERT(Thread, thread);
AVERT(Arena, arena);
RingRemove(&thread->arenaRing);
thread->sig = SigInvalid;
RingFinish(&thread->arenaRing);
PThreadextFinish(&thread->thrextStruct);
ControlFree(arena, thread, sizeof(ThreadStruct));
}
/* mapThreadRing -- map over threads on ring calling a function on each one
* except the current thread
*/
static void mapThreadRing(Ring threadRing, void (*func)(Thread))
{
Ring node, next;
pthread_t self;
AVERT(Ring, threadRing);
self = pthread_self();
RING_FOR(node, threadRing, next) {
Thread thread = RING_ELT(Thread, arenaRing, node);
AVERT(Thread, thread);
if(! pthread_equal(self, thread->id)) /* .thread.id */
(*func)(thread);
}
}
/* ThreadRingSuspend -- suspend all threads on a ring, expect the current one */
static void threadSuspend(Thread thread)
{
/* .error.suspend */
/* In the error case (PThreadextSuspend returning ResFAIL), we */
/* assume the thread has been destroyed. */
/* In which case we simply continue. */
Res res;
res = PThreadextSuspend(&thread->thrextStruct, &thread->mfc);
if(res != ResOK)
thread->mfc = NULL;
}
void ThreadRingSuspend(Ring threadRing)
{
mapThreadRing(threadRing, threadSuspend);
}
/* ThreadRingResume -- resume all threads on a ring (expect the current one) */
static void threadResume(Thread thread)
{
/* .error.resume */
/* If the previous suspend failed (thread->mfc == NULL), */
/* or in the error case (PThreadextResume returning ResFAIL), */
/* assume the thread has been destroyed. */
/* In which case we simply continue. */
if(thread->mfc != NULL) {
(void)PThreadextResume(&thread->thrextStruct);
thread->mfc = NULL;
}
}
void ThreadRingResume(Ring threadRing)
{
mapThreadRing(threadRing, threadResume);
}
/* ThreadRingThread -- return the thread at the given ring element */
Thread ThreadRingThread(Ring threadRing)
{
Thread thread;
AVERT(Ring, threadRing);
thread = RING_ELT(Thread, arenaRing, threadRing);
AVERT(Thread, thread);
return thread;
}
/* ThreadArena -- get the arena of a thread
*
* Must be thread-safe. See design.mps.interface.c.thread-safety.
*/
Arena ThreadArena(Thread thread)
{
/* Can't check thread as that would not be thread-safe. */
return thread->arena;
}
/* ThreadScan -- scan the state of a thread (stack and regs) */
Res ThreadScan(ScanState ss, Thread thread, void *stackBot)
{
pthread_t self;
Res res;
AVERT(Thread, thread);
self = pthread_self();
if(pthread_equal(self, thread->id)) {
/* scan this thread's stack */
res = StackScan(ss, stackBot);
if(res != ResOK)
return res;
} else {
MutatorFaultContext mfc;
Addr *stackBase, *stackLimit, stackPtr;
mfc = thread->mfc;
if(mfc == NULL) {
/* .error.suspend */
/* We assume that the thread must have been destroyed. */
/* We ignore the situation by returning immediately. */
return ResOK;
}
stackPtr = (Addr)mfc->ucontext->uc_mcontext.mc_esp; /* .i3.sp */
/* .stack.align */
stackBase = (Addr *)AddrAlignUp(stackPtr, sizeof(Addr));
stackLimit = (Addr *)stackBot;
if (stackBase >= stackLimit)
return ResOK; /* .stack.below-bottom */
/* scan stack inclusive of current sp and exclusive of
* stackBot (.stack.full-descend)
*/
res = TraceScanAreaTagged(ss, stackBase, stackLimit);
if(res != ResOK)
return res;
/* (.context.regroots)
* This scans the root registers (.context.regroots). It also
* unecessarily scans the rest of the context. The optimisation
* to scan only relevent parts would be machine dependent.
*/
res = TraceScanAreaTagged(ss, (Addr *)mfc->ucontext,
(Addr *)((char *)mfc->ucontext + sizeof(*(mfc->ucontext))));
if(res != ResOK)
return res;
}
return ResOK;
}
/* ThreadDescribe -- describe a thread */
Res ThreadDescribe(Thread thread, mps_lib_FILE *stream)
{
Res res;
res = WriteF(stream,
"Thread $P ($U) {\n", (WriteFP)thread, (WriteFU)thread->serial,
" arena $P ($U)\n",
(WriteFP)thread->arena, (WriteFU)thread->arena->serial,
" id $U\n", (WriteFU)thread->id,
"} Thread $P ($U)\n", (WriteFP)thread, (WriteFU)thread->serial,
NULL);
if(res != ResOK)
return res;
return ResOK;
}

View file

@ -21,14 +21,14 @@
* i.e. stack pointer points to the last allocated location;
* stack grows downwards.
*
* .stack.below-bottom: it's legal for the stack pointer to be at a
* higher address than the registered bottom of stack. This might
* happen if the stack of another thread doesn't contain any frames
* .stack.below-bottom: it's legal for the stack pointer to be at a
* higher address than the registered bottom of stack. This might
* happen if the stack of another thread doesn't contain any frames
* belonging to the client language. In this case, the stack should
* not be scanned.
*
* .stack.align: assume roots on the stack are always word-aligned,
* but don't assume that the stack pointer is necessarily
* but don't assume that the stack pointer is necessarily
* word-aligned at the time of reading the context of another thread.
*
* .sp: The stack pointer in the context is ESP.
@ -58,7 +58,7 @@ typedef struct ThreadStruct { /* PThreads thread structure */
RingStruct arenaRing; /* threads attached to arena */
PThreadextStruct thrextStruct; /* PThreads extension */
pthread_t id; /* Pthread object of thread */
struct sigcontext *scpSusp; /* Context if thread is suspended */
MutatorFaultContext mfc; /* Context if thread is suspended */
} ThreadStruct;
@ -92,9 +92,9 @@ Res ThreadRegister(Thread *threadReturn, Arena arena)
AVER(threadReturn != NULL);
AVERT(Arena, arena);
res = ControlAlloc(&p, arena, sizeof(ThreadStruct),
res = ControlAlloc(&p, arena, sizeof(ThreadStruct),
/* withReservoirPermit */ FALSE);
if(res != ResOK)
if(res != ResOK)
return res;
thread = (Thread)p;
@ -106,7 +106,7 @@ Res ThreadRegister(Thread *threadReturn, Arena arena)
thread->serial = arena->threadSerial;
++arena->threadSerial;
thread->arena = arena;
thread->scpSusp = NULL;
thread->mfc = NULL;
PThreadextInit(&thread->thrextStruct, thread->id);
@ -169,9 +169,9 @@ static void threadSuspend(Thread thread)
/* assume the thread has been destroyed. */
/* In which case we simply continue. */
Res res;
res = PThreadextSuspend(&thread->thrextStruct, &thread->scpSusp);
res = PThreadextSuspend(&thread->thrextStruct, &thread->mfc);
if(res != ResOK)
thread->scpSusp = NULL;
thread->mfc = NULL;
}
@ -188,13 +188,13 @@ void ThreadRingSuspend(Ring threadRing)
static void threadResume(Thread thread)
{
/* .error.resume */
/* If the previous suspend failed (thread->scpSusp == NULL), */
/* If the previous suspend failed (thread->mfc == NULL), */
/* or in the error case (PThreadextResume returning ResFAIL), */
/* assume the thread has been destroyed. */
/* In which case we simply continue. */
if(thread->scpSusp != NULL) {
if(thread->mfc != NULL) {
(void)PThreadextResume(&thread->thrextStruct);
thread->scpSusp = NULL;
thread->mfc = NULL;
}
}
@ -246,7 +246,7 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot)
struct sigcontext *scp;
Addr *stackBase, *stackLimit, stackPtr;
scp = thread->scpSusp;
scp = thread->mfc->scp;
if(scp == NULL) {
/* .error.suspend */
/* We assume that the thread must have been destroyed. */
@ -288,15 +288,15 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot)
Res ThreadDescribe(Thread thread, mps_lib_FILE *stream)
{
Res res;
res = WriteF(stream,
"Thread $P ($U) {\n", (WriteFP)thread, (WriteFU)thread->serial,
" arena $P ($U)\n",
" arena $P ($U)\n",
(WriteFP)thread->arena, (WriteFU)thread->arena->serial,
" id $U\n", (WriteFU)thread->id,
"} Thread $P ($U)\n", (WriteFP)thread, (WriteFU)thread->serial,
NULL);
if(res != ResOK)
if(res != ResOK)
return res;
return ResOK;

278
mps/code/vmfr.c Normal file
View file

@ -0,0 +1,278 @@
/* impl.c.vmfr: VIRTUAL MEMORY MAPPING FOR FreeBSD
*
* $HopeName: MMsrc!vmli.c(trunk.7) $
* Copyright (C) 2000 Harlequin Limited. All rights reserved.
*
* .purpose: This is the implementation of the virtual memory mapping
* interface (vm.h) for FreeBSD. It was created by copying vmli.c (the
* DIGITAL UNIX implementation) as that seemed to be closest.
*
* .design: See design.mps.vm. .design.freebsd: mmap(2) is used to
* reserve address space by creating a mapping with page access none.
* mmap(2) is used to map pages onto store by creating a copy-on-write
* (MAP_PRIVATE) mapping with the flag MAP_ANON.
*
* .assume.not-last: The implementation of VMCreate assumes that
* mmap() will not choose a region which contains the last page
* in the address space, so that the limit of the mapped area
* is representable.
*
* .assume.mmap.err: ENOMEM is the only error we really expect to
* get from mmap. The others are either caused by invalid params
* or features we don't use. See mmap(2) for details.
*
* .remap: Possibly this should use mremap to reduce the number of
* distinct mappings. According to our current testing, it doesn't
* seem to be a problem.
*/
/* for mmap(2), munmap(2) */
#include <sys/types.h>
#include <sys/mman.h>
/* for errno(2) */
#include <errno.h>
/* for getpagesize(3) */
#include <unistd.h>
#include "mpm.h"
#ifndef MPS_OS_FR
#error "vmfr.c is FreeBSD specific, but MPS_OS_FR is not set"
#endif
SRCID(vmfr, "$HopeName: MMsrc!vmli.c(trunk.7) $");
/* VMStruct -- virtual memory structure */
#define VMSig ((Sig)0x519B3999) /* SIGnature VM */
typedef struct VMStruct {
Sig sig; /* design.mps.sig */
Align align; /* page size */
Addr base, limit; /* boundaries of reserved space */
Size reserved; /* total reserved address space */
Size mapped; /* total mapped memory */
} VMStruct;
/* VMAlign -- return page size */
Align VMAlign(VM vm)
{
return vm->align;
}
/* VMCheck -- check a VM */
Bool VMCheck(VM vm)
{
CHECKS(VM, vm);
CHECKL(vm->base != 0);
CHECKL(vm->limit != 0);
CHECKL(vm->base < vm->limit);
CHECKL(vm->mapped <= vm->reserved);
CHECKL(SizeIsP2(vm->align));
CHECKL(AddrIsAligned(vm->base, vm->align));
CHECKL(AddrIsAligned(vm->limit, vm->align));
return TRUE;
}
/* VMCreate -- reserve some virtual address space, and create a VM structure */
Res VMCreate(VM *vmReturn, Size size)
{
Align align;
VM vm;
int pagesize;
void *addr;
Res res;
AVER(vmReturn != NULL);
/* Find out the page size from the OS */
pagesize = getpagesize();
/* check the actual returned pagesize will fit in an object of */
/* type Align. */
AVER(pagesize > 0);
AVER((unsigned long)pagesize <= (unsigned long)(Align)-1);
/* Note implicit conversion from "int" to "Align". */
align = pagesize;
AVER(SizeIsP2(align));
size = SizeAlignUp(size, align);
if((size == 0) || (size > (Size)(size_t)-1))
return ResRESOURCE;
/* Map in a page to store the descriptor on. */
addr = mmap(0, (size_t)SizeAlignUp(sizeof(VMStruct), align),
PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE,
-1, 0);
if(addr == MAP_FAILED) {
int e = errno;
AVER(e == ENOMEM); /* .assume.mmap.err */
return ResMEMORY;
}
vm = (VM)addr;
vm->align = align;
/* See .assume.not-last. */
addr = mmap(0, (size_t)size,
PROT_NONE, MAP_ANON | MAP_PRIVATE,
-1, 0);
if(addr == MAP_FAILED) {
int e = errno;
AVER(e == ENOMEM); /* .assume.mmap.err */
res = ResRESOURCE;
goto failReserve;
}
vm->base = (Addr)addr;
vm->limit = AddrAdd(vm->base, size);
vm->reserved = size;
vm->mapped = (Size)0;
vm->sig = VMSig;
AVERT(VM, vm);
EVENT_PAA(VMCreate, vm, vm->base, vm->limit);
*vmReturn = vm;
return ResOK;
failReserve:
(void)munmap((void *)vm, (size_t)SizeAlignUp(sizeof(VMStruct), align));
return res;
}
/* VMDestroy -- release all address space and destroy VM structure */
void VMDestroy(VM vm)
{
int r;
AVERT(VM, vm);
AVER(vm->mapped == (Size)0);
/* This appears to be pretty pointless, since the descriptor */
/* page is about to vanish completely. However, munmap might fail */
/* for some reason, and this would ensure that it was still */
/* discovered if sigs were being checked. */
vm->sig = SigInvalid;
r = munmap((void *)vm->base, (size_t)AddrOffset(vm->base, vm->limit));
AVER(r == 0);
r = munmap((void *)vm,
(size_t)SizeAlignUp(sizeof(VMStruct), vm->align));
AVER(r == 0);
EVENT_P(VMDestroy, vm);
}
/* VMBase -- return the base address of the memory reserved */
Addr VMBase(VM vm)
{
AVERT(VM, vm);
return vm->base;
}
/* VMLimit -- return the limit address of the memory reserved */
Addr VMLimit(VM vm)
{
AVERT(VM, vm);
return vm->limit;
}
/* VMReserved -- return the amount of memory reserved */
Size VMReserved(VM vm)
{
AVERT(VM, vm);
return vm->reserved;
}
/* VMMapped -- return the amount of memory actually mapped */
Size VMMapped(VM vm)
{
AVERT(VM, vm);
return vm->mapped;
}
/* VMMap -- map the given range of memory */
Res VMMap(VM vm, Addr base, Addr limit)
{
Size size;
AVERT(VM, vm);
AVER(sizeof(void *) == sizeof(Addr));
AVER(base < limit);
AVER(base >= vm->base);
AVER(limit <= vm->limit);
AVER(AddrIsAligned(base, vm->align));
AVER(AddrIsAligned(limit, vm->align));
size = AddrOffset(base, limit);
if(mmap((void *)base, (size_t)size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE | MAP_FIXED,
-1, 0)
== MAP_FAILED) {
AVER(errno == ENOMEM); /* .assume.mmap.err */
return ResMEMORY;
}
vm->mapped += size;
EVENT_PAA(VMMap, vm, base, limit);
return ResOK;
}
/* VMUnmap -- unmap the given range of memory */
void VMUnmap(VM vm, Addr base, Addr limit)
{
Size size;
void *addr;
AVERT(VM, vm);
AVER(base < limit);
AVER(base >= vm->base);
AVER(limit <= vm->limit);
AVER(AddrIsAligned(base, vm->align));
AVER(AddrIsAligned(limit, vm->align));
size = AddrOffset(base, limit);
/* see design.mps.vmo1.fun.unmap.offset */
addr = mmap((void *)base, (size_t)size,
PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_FIXED,
-1, 0);
AVER(addr == (void *)base);
vm->mapped -= size;
EVENT_PAA(VMUnmap, vm, base, limit);
}