summaryrefslogtreecommitdiff
path: root/sys/src/cmd/5e/seg.c
blob: caa4002ac763c5693bb7bb5bfd0afb5a5ceab0c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <u.h>
#include <libc.h>
#include <thread.h>
#include "dat.h"
#include "fns.h"

Segment *
newseg(u32int start, u32int size, int idx)
{
	Segment *s;
	
	s = emallocz(sizeof *s);
	incref(s);
	s->start = start;
	s->size = size;
	s->ref = emalloc(size + sizeof(Ref));
	memset(s->ref, 0, sizeof(Ref));
	incref(s->ref);
	s->data = s->ref + 1;
	if(idx == SEGBSS)
		s->flags = SEGFLLOCK;
	P->S[idx] = s;
	return s;
}

void
freesegs(void)
{
	Segment **s;
	
	for(s = P->S; s < P->S + SEGNUM; s++) {
		if(*s == nil)
			continue;
		if(decref((*s)->ref) == 0)
			free((*s)->ref);
		if(decref(*s) == 0)
			free(*s);
		*s = nil;
	}
}

void *
vaddr(u32int addr, Segment **seg)
{
	Segment **ss, *s;

	for(ss = P->S; ss < P->S + SEGNUM; ss++) {
		if(*ss == nil)
			continue;
		s = *ss;
		if(addr >= s->start && addr < s->start + s->size) {
			if(s->flags & SEGFLLOCK)
				rlock(&s->rw);
			*seg = s;
			return (char *)s->data + (addr - s->start);
		}
	}
	sysfatal("fault %.8ux @ %.8ux", addr, P->R[15]);
	return nil;
}

void *
vaddrnol(u32int addr)
{
	Segment *seg;
	void *ret;
	
	ret = vaddr(addr, &seg);
	segunlock(seg);
	return ret;
}

/* might be made a macro for hurr durr performance */
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, &seg);
	if((seg->flags & SEGFLLOCK) == 0) {
		*copied = 0;
		return targ;
	}
	if(len < 0)
		len = strlen(targ) + 1;
	ret = emalloc(len);
	memcpy(ret, targ, len);
	segunlock(seg);
	*copied = 1;
	return ret;
}

void *
bufifnec(u32int addr, int len, int *buffered)
{
	void *targ;
	Segment *seg;
	
	targ = vaddr(addr, &seg);
	if((seg->flags & SEGFLLOCK) == 0) {
		*buffered = 0;
		return targ;
	}
	segunlock(seg);
	*buffered = 1;
	return emalloc(len);
}

void
copyback(u32int addr, int len, void *data)
{
	void *targ;
	Segment *seg;

	if(len <= 0)
		return;
	targ = vaddr(addr, &seg);
	memmove(targ, data, len);
	segunlock(seg);
	free(data);
}