Plan 9 from Bell Labs’s /usr/web/sources/contrib/cinap_lenrek/old/linuxemu.old/mmap.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include "linuxsys.h"
#include "linux.h"

typedef struct Seg Seg;
struct Seg
{
	ulong	base;
	ulong	top;
	ulong	brk;
};

typedef struct Filemap Filemap;
struct Filemap 
{
	Filemap	*next;

	ulong	base;
	ulong	top;

	int		fd;
	int		offset;
	int		prot;
	int		flags;
};

enum
{
	PTSTACKSEGSIZE	= 0x10000000,
	MAXSHARESEGSIZE	= 0x10000000,
};

enum
{
	SEG_STACK,
	SEG_TEXT,
	SEG_DATA,
	SEG_BSS,
	SEG_MMAP,
	SEG_SHARE,
	SEG_PTSTACK,
	SEG_MAX,
};

static char *segname[] = {
	"STACK",
	"TEXT",
	"DATA",
	"BSS",
	"MMAP",
	"SHARE",
	"PTSTACK",
};

static Seg segs[SEG_MAX];
static QLock seglock;

static Filemap *filemaps;

void
mmapinit(void)
{
	char buf[80];
	int fd;
	int n;
	int i;

	DPRINT("mmapinit\n");
	snprint(buf, sizeof(buf), "/proc/%d/segment", getpid());
	if((fd = open(buf, OREAD)) < 0){
		fprint(2, "cannot read segment file from proc: %r\n");
		exits("open");
	}

	for(i=0; i<SEG_MAX; i++){
		segs[i].base = 0;
		segs[i].top = 0;
		segs[i].brk = 0;
	}
	
	n = 10 + 9 + 9 + 4 + 1;
	while(readn(fd, buf, n)==n){
		int x;

		buf[9] = 0;
		buf[19] = 0;
		buf[28] = 0;
		buf[33] = 0;

		x = -1;
		if(strstr(&buf[0], "Stack"))
			x = SEG_STACK;
		if(strstr(&buf[0], "Text"))
			x = SEG_TEXT;
		if(strstr(&buf[0], "Data"))
			x = SEG_DATA;
		if(strstr(&buf[0], "Bss"))
			x = SEG_BSS;

		segs[x].base	= strtoul(&buf[10], nil, 16);
		segs[x].top	= strtoul(&buf[20], nil, 16);

		DPRINT("mmapinit seg %d/\"%s\" 0x%lux-0x%lux\n", 
			x, &buf[0], segs[x].base, segs[x].top);
	}

	segs[SEG_PTSTACK].top	= segs[SEG_STACK].base;
	segs[SEG_PTSTACK].base	= segs[SEG_PTSTACK].top - PTSTACKSEGSIZE;
	segs[SEG_PTSTACK].brk	= segs[SEG_PTSTACK].top;

	if(segattach(SG_CEXEC, "memory", (void*)segs[SEG_PTSTACK].base, 
		PTSTACKSEGSIZE)==(void*)-1){

		fprint(2, "mmap: allocating ptstackseg failed: %r\n");
		exits("segattach");
	}
	DPRINT("mmapinit done\n");
}

void
mmapexit(void)
{
	int i;

	qlock(&seglock);
	for(i=0; i<SEG_MAX; i++){
		void *v;
		switch(i){
		case SEG_MMAP:
		case SEG_PTSTACK:
			if(v = (void*)segs[i].base)
				segdetach(v);
			segs[i].base = 0;
			segs[i].top = 0;
			segs[i].brk = 0;
			break;
		}
	}
	qunlock(&seglock);
}

static int
findseg(ulong a)
{
	int x;
	for(x = 0; x<SEG_MAX; x++){
		if(a < segs[x].base)
			continue;
		if(a >= segs[x].top)
			continue;
		return x;
	}
	return -1;
}

void*
allocstack(int size)
{
	void *top;

	size = ROUND_PAGE(size);

	qlock(&seglock);
	if(segs[SEG_PTSTACK].brk - size < segs[SEG_PTSTACK].base){
		qunlock(&seglock);
		return nil;
	}
	top = (void*)segs[SEG_PTSTACK].brk;
	segs[SEG_PTSTACK].brk -= size;
	qunlock(&seglock);

	return top;
}


static void
addfilemap(Filemap **list, Filemap *map)
{
	Filemap **i;

	for(i=list; *i; i=&((*i)->next)){
		if((*i)->base > map->base){
			map->next = *i;
			*i = map;
			return;
		}
	}

	/* just add to the tail */
	map->next = nil;
	*i = map;
}


static Filemap **
findfilemap(Filemap **list, ulong addr)
{
	Filemap **i;

	for(i=list; *i; i=&((*i)->next)){
		if(addr >= (*i)->base && addr < (*i)->top)
			return i;
	}
	return nil;
}

void*
mmap(void *addr, int len, int prot, int flags, int fd, int offset)
{
	ulong ret;
	ulong maplen;
	int x;
	void *v;

	DPRINT("mmap(0x%p, %d, 0x%x, 0x%x, %d, %d)...", addr, len, prot, flags, fd, offset);

	/* make sure addr is page aligned */
	if(TRUNC_PAGE((ulong)addr)!=(ulong)addr)
		return (void*)-1;
	maplen = ROUND_PAGE(len);

	if(	((ulong)addr >= segs[SEG_STACK].base) && 
		((ulong)addr + maplen) <= segs[SEG_STACK].top){
		DPRINT("mmap in stack segment!\n");
		return (void*)-1;
	}

	qlock(&seglock);

	if(flags & MAP_SHARED){
		x = SEG_SHARE;
	} else {
		x = SEG_MMAP;
	}

	/* initialize segments to PAGE_SIZE on first request */
	if(segs[x].base == 0){
		if(x == SEG_SHARE){
			if((flags & MAP_FIXED)==0 || addr==nil){
				addr = (void*)(segs[SEG_PTSTACK].base - MAXSHARESEGSIZE);
			}
		} else {
			if(addr==nil)
				addr = (void*)0x84000000;
		}
		if((v = segattach(
			SG_CEXEC, (x == SEG_SHARE) ? "shared" : "memory",
			addr, PAGE_SIZE)) == (void*)-1){
			qunlock(&seglock);
			return (void*)-1;
		}
		segs[x].top = segs[x].base = (ulong)v;
		segs[x].top += PAGE_SIZE;
	}

	/* give pthreads a shared stack segment */
	if(	((ulong)addr >= segs[SEG_PTSTACK].base) && 
		((ulong)addr + maplen) <= segs[SEG_PTSTACK].top){
		if((ulong)addr+maplen <= segs[SEG_PTSTACK].brk){
			ret = (ulong)addr;
			segs[SEG_PTSTACK].brk = ret;
			goto fillmem;
		}
	}

	if(flags & MAP_FIXED){
		ret = (ulong)addr;

		/* fixed mappings below segment are not possible */
		if(ret < segs[x].base){
			qunlock(&seglock);
			return (void*)-1;
		}

		/* need to expand the segment? */
		if(ret + maplen > segs[x].top){
			segs[x].top = ret + maplen;
		} else {
			goto fillmem;
		}
	} else {
		/* just append on segment */
		ret = segs[x].top;
		segs[x].top += maplen;
	}

	/* grow the segment */
	if(segbrk((void*)segs[x].base, (void*)segs[x].top) == (void*)-1){
		qunlock(&seglock);
		fprint(2, "segbrk (grow) for seg: %s map: 0x%p-0x%p failed in mmap: %r", 
			segname[x], segs[x].base, segs[x].top);
		exits("segbrk");
	}

fillmem:
	if((flags & MAP_ANONYMOUS) == 0 && fd >= 0){
			Filemap *fm;

			fm = malloc(sizeof(*fm));
			fm->base = ret;
			fm->top = ret + len;
			fm->fd = fd;
			fm->offset = offset;
			fm->flags = flags;
			fm->prot = prot;
			fm->next = nil;

			addfilemap(&filemaps, fm);
	}
	qunlock(&seglock);
	if(flags & MAP_ANONYMOUS){
		/* programs expect anonymous memory to contain all zeros */
		segfree((void*)ret, maplen);
	} else {
		/* map file in memory */
		if(fd >= 0){
			fd = dup(fd, -1);
			if(debug){
				Dir *d;
				d = dirfstat(fd);
				DPRINT("mapped file \"%s\" at...", d->name);
				free(d);
			}
			seek(fd, offset, 0);
			len = readn(fd, (void*)ret, len);
			close(fd);
		} else {
			len = 0;
		}
		/* len contains what have been read, fill the rest up with zeros */
		if(maplen > len)
			memset((void*)(ret + len), 0, maplen - len);
	}
	DPRINT("0x%lux-0x%lux\n", ret, ret+maplen);
	return (void*)ret;
}

int
munmap(void *addr, int len)
{
	Filemap **fm;
	ulong a;
	ulong l;
	int x;

	DPRINT("munmap(0x%p, %d)...", addr, len);

	a = (ulong)addr;
	if(TRUNC_PAGE(a)!=a)
		return -EINVAL;
	if((l = ROUND_PAGE(len)) == 0)
		return -EINVAL;

	qlock(&seglock);

	if((x = findseg(a)) < 0){
		qunlock(&seglock);
		return 0;
	}

	if(x == SEG_PTSTACK){
		if(a == segs[x].brk)
			segs[x].brk = a + l;
		goto clear;
	}

	while(fm = findfilemap(&filemaps, a)){
		Filemap *map;

		map = *fm;
		*fm = map->next;

		free(map);
	}

	if(a + l > segs[x].top)
		l = segs[x].top - a;

	if(a + l == segs[x].top){
		switch(x){
		case SEG_MMAP:
		case SEG_SHARE:
			segs[x].top = a;

			/* shrink the segment */
			if(segbrk((void*)segs[x].base, 
				(void*)segs[x].top) == (void*)-1){
				qunlock(&seglock);

				fprint(2, "segbrk (shrink) failed for seg %s map: 0x%p-0x%p in munmap: %r",
					segname[x], segs[x].base, segs[x].top);
				exits("segbrk");
			}
			qunlock(&seglock);
			return 0;
		}
	}
clear:
	qunlock(&seglock);
	segfree((void*)a, l);
	return 0;
}

int
msync(void *addr)
{
	Filemap **fm;
	ulong a;

	DPRINT("msync(0x%p)...", addr);

	a = (ulong)addr;
	if(TRUNC_PAGE(a)!=a)
		return -EINVAL;

	qlock(&seglock);
	fm = findfilemap(&filemaps, a);
	if(!fm || !*fm || (*fm)->base != a)
		goto out;


	if((*fm)->prot & PROT_WRITE){
		seek((*fm)->fd, (*fm)->offset, 0);
		write((*fm)->fd, (void*)(*fm)->base, (*fm)->top - (*fm)->base);
	}

out:	
	qunlock(&seglock);
	return 0;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].