ref: cc7216394dd7f921e6856a9ce7fa0344816a222e
dir: /kern/seg.c/
#include <u.h>
#include <lib.h>
#include "dat.h"
#include "fns.h"
Segment *
newseg(u32int start, u32int size, int idx)
{
Segment *s;
if(debug > 1)
print("newseg: size=%.8ux start=%.8ux idx=%d\n", size, start, idx);
s = malloc(sizeof(Segment));
if(s == nil)
panic("%r");
incref(&s->ref);
s->start = start;
s->size = size;
s->dref = malloc(size + sizeof(Ref));
memset(s->dref, 0, sizeof(Ref));
incref(s->dref);
s->data = s->dref + 1;
if(idx == BSEG)
s->flags = SEGFLLOCK;
if(idx == TSEG)
s->type = SG_TEXT | SG_RONLY;
else if(idx == DSEG)
s->type = SG_DATA;
else if(idx == BSEG)
s->type = SG_BSS;
else if(idx == ESEG)
s->type = SG_STACK;
else
panic("newseg: unexpected index %d", idx);
if(debug > 1)
print("newseg: created idx=%d type=%d start=%.8ux size=%.8ux\n",
idx, s->type, s->start, s->size);
return s;
}
void
freesegs(void)
{
Segment **s, *ss;
for(s = up->seg; s < up->seg + NSEG; s++) {
if(*s == nil)
continue;
ss = *s;
// Assure we don't have any weird state
if(!ss->dref || ss->dref->ref == 0 || ss->ref.ref != 0)
continue;
if(decref(ss->dref) == 0)
free(ss->dref);
if(decref(&ss->ref) == 0)
free(*s);
*s = nil;
}
}
void *
vaddr(u32int addr, u32int len, Segment **seg)
{
Segment **ss, *s;
int count = 0;
qlock(&up->seglock);
for (ss = up->seg; ss < up->seg + NSEG; ss++) {
count++;
if (*ss == nil)
continue;
s = *ss;
if (addr >= s->start && addr < s->start + s->size) {
if (addr + len > s->start + s->size)
break;
if (s->flags & SEGFLLOCK)
rlock(&s->rw);
*seg = s;
if(debug > 1)
print("vaddr addr=%.8ux, len=%d, PC=%.8ux, index=%.8ux, segment=%d, type=%d, pid=%d\n",
addr, len, up->R[15], s->start, count - 1, s->type, up->pid);
qunlock(&up->seglock);
return (char *)s->data + (addr - s->start);
}
}
qunlock(&up->seglock);
panic("segfault addr=%.8ux, len=%d, PC=%.8ux, pid=%d\n", addr, len, up->R[15], up->pid);
return nil;
}
Segment*
data2txt(Segment *s)
{
Segment *ps;
if(debug > 1)
print("data2txt: converting start=%.8ux size=%.8ux\n", s->start, s->size);
ps = newseg(s->start, s->size, TSEG);
ps->dref = s->dref;
memcpy(ps->data, s->data, s->size);
return ps;
}
Segment *
dupseg(Segment **seg, int segno, int share)
{
Segment *n, *s;
if(segno < 0 || segno >= NSEG)
panic("dupseg: invalid segment index %d", segno);
s = seg[segno];
if(debug > 1)
print("dupseg: segno=%d, type=%d, start=%.8ux, size=%.8ux, share=%d\n", segno, s->type, s->start, s->size, share);
rlock(&s->rw);
if(waserror()) {
runlock(&s->rw);
nexterror();
}
switch(s->type&SG_TYPE) {
case SG_TEXT:
case SG_SHARED:
case SG_PHYSICAL:
case SG_FIXED:
case SG_STICKY:
default:
goto sameseg;
case SG_STACK:
n = newseg(s->start, s->size, ESEG);
break;
case SG_BSS: /* Just copy on write */
if(share)
goto sameseg;
n = newseg(s->start, s->size, BSEG);
break;
case SG_DATA: /* Copy on write plus demand load info */
if(segno == TSEG){
n = data2txt(s);
runlock(&s->rw);
poperror();
return n;
}
if(share)
goto sameseg;
n = newseg(s->start, s->size, DSEG);
break;
}
if(n != nil && n != s)
memcpy(n->data, s->data, s->size);
runlock(&s->rw);
poperror();
return n;
sameseg:
incref(&s->ref);
runlock(&s->rw);
poperror();
return s;
}
void*
vaddrnol(u32int addr, u32int len)
{
Segment *seg;
void *ret;
ret = vaddr(addr, len, &seg);
segunlock(seg);
return ret;
}
void
segunlock(Segment *s)
{
if(s->flags & SEGFLLOCK)
runlock(&s->rw);
}
void *
copyifnec(u32int addr, int len, int *copied)
{
void *targ, *ret;
Segment *seg;
targ = vaddr(addr, len > 0 ? len : 0, &seg);
if((seg->flags & SEGFLLOCK) == 0) {
*copied = 0;
return targ;
}
if(len < 0)
len = strlen(targ) + 1;
ret = mallocz(len, 1);
if (ret == nil)
panic("%r");
setmalloctag(ret, getcallerpc(&addr));
memcpy(ret, targ, len);
segunlock(seg);
*copied = 1;
return ret;
}
void *
bufifnec(u32int addr, int len, int *buffered)
{
void *targ, *v;
Segment *seg;
targ = vaddr(addr, len, &seg);
if((seg->flags & SEGFLLOCK) == 0) {
*buffered = 0;
return targ;
}
segunlock(seg);
*buffered = 1;
v = mallocz(len, 1);
if (v == nil)
panic("%r");
setmalloctag(v, getcallerpc(&addr));
return v;
}
void
copyback(u32int addr, int len, void *data)
{
void *targ;
Segment *seg;
if(len <= 0) {
free(data);
return;
}
targ = vaddr(addr, len, &seg);
memmove(targ, data, len);
segunlock(seg);
free(data);
}