shithub: drawcpu

ref: f6da1ab12d14fe4db7a76f390d6dfc85de660f1c
dir: /kern/seg.c/

View raw version
#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);
	qlock(&s->rw);
	if(waserror()) {
		qunlock(&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);
			qunlock(&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);
	qunlock(&s->rw);
	poperror();
	return n;
sameseg:
	incref(s);
	qunlock(&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);
}