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