#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;
}
|