xref: /plan9-contrib/sys/src/9k/port/sysseg.c (revision 0d74731bc0e92f599316bb993220a7ecf480c626)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7 
8 Segment* (*_globalsegattach)(Proc*, char*);
9 
10 static Lock physseglock;
11 
12 int
addphysseg(Physseg * new)13 addphysseg(Physseg* new)
14 {
15 	Physseg *ps;
16 
17 	/*
18 	 * Check not already entered and there is room
19 	 * for a new entry and the terminating null entry.
20 	 */
21 	lock(&physseglock);
22 	for(ps = physseg; ps->name; ps++){
23 		if(strcmp(ps->name, new->name) == 0){
24 			unlock(&physseglock);
25 			return -1;
26 		}
27 	}
28 	if(ps-physseg >= nphysseg-2){
29 		unlock(&physseglock);
30 		return -1;
31 	}
32 
33 	*ps = *new;
34 	unlock(&physseglock);
35 
36 	return 0;
37 }
38 
39 int
isphysseg(char * name)40 isphysseg(char *name)
41 {
42 	int rv;
43 	Physseg *ps;
44 
45 	lock(&physseglock);
46 	rv = 0;
47 	for(ps = physseg; ps->name; ps++){
48 		if(strcmp(ps->name, name) == 0){
49 			rv = 1;
50 			break;
51 		}
52 	}
53 	unlock(&physseglock);
54 	return rv;
55 }
56 
57 /* Needs to be non-static for BGP support */
58 uintptr
ibrk(uintptr addr,int seg)59 ibrk(uintptr addr, int seg)
60 {
61 	Segment *s, *ns;
62 	uintptr newtop, pgsize;
63 	usize newsize;
64 	int i, mapsize;
65 	Pte **map;
66 
67 	s = up->seg[seg];
68 	if(s == nil)
69 		error(Ebadarg);
70 
71 	if(addr == 0)
72 		return s->top;
73 
74 	qlock(&s->lk);
75 	if(waserror()){
76 		qunlock(&s->lk);
77 		nexterror();
78 	}
79 
80 	/* We may start with the bss overlapping the data */
81 	if(addr < s->base) {
82 		if(seg != BSEG || up->seg[DSEG] == nil || addr < up->seg[DSEG]->base)
83 			error(Enovmem);
84 		addr = s->base;
85 	}
86 
87 	pgsize = segpgsize(s);
88 	newtop = ROUNDUP(addr, pgsize);
89 	newsize = (newtop-s->base)/pgsize;
90 	if(newtop < s->top) {
91 		/*
92 		 * do not shrink a segment shared with other procs, as the
93 		 * to-be-freed address space may have been passed to the kernel
94 		 * already by another proc and is past the validaddr stage.
95 		 */
96 		if(s->ref > 1)
97 			error(Einuse);
98 		mfreeseg(s, newtop, s->top);
99 		s->top = newtop;
100 		poperror();
101 		s->size = newsize;
102 		qunlock(&s->lk);
103 		mmuflush();
104 		return newtop;
105 	}
106 
107 	for(i = 0; i < NSEG; i++) {
108 		ns = up->seg[i];
109 		if(ns == nil || ns == s)
110 			continue;
111 		if(newtop > ns->base && s->base < ns->top)
112 			error(Esoverlap);
113 	}
114 
115 	mapsize = HOWMANY(newsize, PTEPERTAB);
116 	if(mapsize > SEGMAPSIZE)
117 		error(Enovmem);
118 	if(mapsize > s->mapsize){
119 		map = smalloc(mapsize*sizeof(Pte*));
120 		memmove(map, s->map, s->mapsize*sizeof(Pte*));
121 		if(s->map != s->ssegmap)
122 			free(s->map);
123 		s->map = map;
124 		s->mapsize = mapsize;
125 	}
126 
127 	s->top = newtop;
128 	s->size = newsize;
129 
130 	poperror();
131 	qunlock(&s->lk);
132 
133 	return newtop;
134 }
135 
136 void
syssegbrk(Ar0 * ar0,va_list list)137 syssegbrk(Ar0* ar0, va_list list)
138 {
139 	int i;
140 	uintptr addr;
141 	Segment *s;
142 
143 	/*
144 	 * int segbrk(void*, void*);
145 	 * should be
146 	 * void* segbrk(void* saddr, void* addr);
147 	 */
148 	addr = PTR2UINT(va_arg(list, void*));
149 	for(i = 0; i < NSEG; i++) {
150 		s = up->seg[i];
151 		if(s == nil || addr < s->base || addr >= s->top)
152 			continue;
153 		switch(s->type&SG_TYPE) {
154 		case SG_TEXT:
155 		case SG_DATA:
156 		case SG_STACK:
157 			error(Ebadarg);
158 		default:
159 			addr = PTR2UINT(va_arg(list, void*));
160 			ar0->v = UINT2PTR(ibrk(addr, i));
161 			return;
162 		}
163 	}
164 	error(Ebadarg);
165 }
166 
167 void
sysbrk_(Ar0 * ar0,va_list list)168 sysbrk_(Ar0* ar0, va_list list)
169 {
170 	uintptr addr;
171 
172 	/*
173 	 * int brk(void*);
174 	 *
175 	 * Deprecated; should be for backwards compatibility only.
176 	 */
177 	addr = PTR2UINT(va_arg(list, void*));
178 
179 	ibrk(addr, BSEG);
180 
181 	ar0->i = 0;
182 }
183 
184 static uintptr
segattach(Proc * p,int attr,char * name,uintptr va,usize len)185 segattach(Proc* p, int attr, char* name, uintptr va, usize len)
186 {
187 	int sno;
188 	Segment *s, *os;
189 	Physseg *ps;
190 	uintptr pgsize;
191 
192 	/* BUG: Only ok for now */
193 	if((va != 0 && va < UTZERO) || iskaddr(va))
194 		error("virtual address in kernel");
195 
196 	vmemchr(name, 0, ~0);
197 
198 	for(sno = 0; sno < NSEG; sno++)
199 		if(p->seg[sno] == nil && sno != ESEG)
200 			break;
201 
202 	if(sno == NSEG)
203 		error("too many segments in process");
204 
205 	/*
206 	 *  first look for a global segment with the
207 	 *  same name
208 	 */
209 	if(_globalsegattach != nil){
210 		s = (*_globalsegattach)(p, name);
211 		if(s != nil){
212 			p->seg[sno] = s;
213 			return s->base;
214 		}
215 	}
216 
217 	for(ps = physseg; ps->name; ps++)
218 		if(strcmp(name, ps->name) == 0)
219 			goto found;
220 
221 	error("segment not found");
222 found:
223 	pgsize = physsegpgsize(ps);
224 	len = ROUNDUP(len, pgsize);
225 	if(len == 0)
226 		error("length overflow");
227 
228 	/*
229 	 * Find a hole in the address space.
230 	 * Starting at the lowest possible stack address - len,
231 	 * check for an overlapping segment, and repeat at the
232 	 * base of that segment - len until either a hole is found
233 	 * or the address space is exhausted.
234 	 */
235 //need check here to prevent mapping page 0?
236 	if(va == 0) {
237 		va = p->seg[SSEG]->base - len;
238 		for(;;) {
239 			os = isoverlap(p, va, len);
240 			if(os == nil)
241 				break;
242 			va = os->base;
243 			if(len > va)
244 				error("cannot fit segment at virtual address");
245 			va -= len;
246 		}
247 	}
248 
249 	va = va&~(pgsize-1);
250 	if(isoverlap(p, va, len) != nil)
251 		error(Esoverlap);
252 
253 	if((len/pgsize) > ps->size)
254 		error("len > segment size");
255 
256 	attr &= ~SG_TYPE;		/* Turn off what is not allowed */
257 	attr |= ps->attr;		/* Copy in defaults */
258 
259 	s = newseg(attr, va, va+len);
260 	s->pseg = ps;
261 	p->seg[sno] = s;
262 
263 	return va;
264 }
265 
266 void
syssegattach(Ar0 * ar0,va_list list)267 syssegattach(Ar0* ar0, va_list list)
268 {
269 	int attr;
270 	char *name;
271 	uintptr va;
272 	usize len;
273 
274 	/*
275 	 * long segattach(int, char*, void*, ulong);
276 	 * should be
277 	 * void* segattach(int, char*, void*, usize);
278 	 */
279 	attr = va_arg(list, int);
280 	name = va_arg(list, char*);
281 	va = PTR2UINT(va_arg(list, void*));
282 	len = va_arg(list, usize);
283 
284 	ar0->v = UINT2PTR(segattach(up, attr, validaddr(name, 1, 0), va, len));
285 }
286 
287 void
syssegdetach(Ar0 * ar0,va_list list)288 syssegdetach(Ar0* ar0, va_list list)
289 {
290 	int i;
291 	uintptr addr;
292 	Segment *s;
293 
294 	/*
295 	 * int segdetach(void*);
296 	 */
297 	addr = PTR2UINT(va_arg(list, void*));
298 
299 	qlock(&up->seglock);
300 	if(waserror()){
301 		qunlock(&up->seglock);
302 		nexterror();
303 	}
304 
305 	s = 0;
306 	for(i = 0; i < NSEG; i++)
307 		if(s = up->seg[i]) {
308 			qlock(&s->lk);
309 			if((addr >= s->base && addr < s->top) ||
310 			   (s->top == s->base && addr == s->base))
311 				goto found;
312 			qunlock(&s->lk);
313 		}
314 
315 	error(Ebadarg);
316 
317 found:
318 	/*
319 	 * Can't detach the initial stack segment
320 	 * because the clock writes profiling info
321 	 * there.
322 	 */
323 	if(s == up->seg[SSEG]){
324 		qunlock(&s->lk);
325 		error(Ebadarg);
326 	}
327 	up->seg[i] = 0;
328 	qunlock(&s->lk);
329 	putseg(s);
330 	qunlock(&up->seglock);
331 	poperror();
332 
333 	/* Ensure we flush any entries from the lost segment */
334 	mmuflush();
335 
336 	ar0->i = 0;
337 }
338 
339 void
syssegfree(Ar0 * ar0,va_list list)340 syssegfree(Ar0* ar0, va_list list)
341 {
342 	Segment *s;
343 	uintptr from, to, pgsize;
344 	usize len;
345 
346 	/*
347 	 * int segfree(void*, ulong);
348 	 * should be
349 	 * int segfree(void*, usize);
350 	 */
351 	from = PTR2UINT(va_arg(list, void*));
352 	s = seg(up, from, 1);
353 	if(s == nil)
354 		error(Ebadarg);
355 	len = va_arg(list, usize);
356 	pgsize = segpgsize(s);
357 	to = (from + len) & ~(pgsize-1);
358 	if(to < from || to > s->top){
359 		qunlock(&s->lk);
360 		error(Ebadarg);
361 	}
362 	from = ROUNDUP(from, pgsize);
363 
364 	mfreeseg(s, from, to);
365 	qunlock(&s->lk);
366 	mmuflush();
367 
368 	ar0->i = 0;
369 }
370 
371 static void
pteflush(Pte * pte,int s,int e)372 pteflush(Pte *pte, int s, int e)
373 {
374 	int i;
375 
376 	for(i = s; i < e; i++)
377 		mmucachectl(pte->pages[i], PG_TXTFLUSH);
378 }
379 
380 void
syssegflush(Ar0 * ar0,va_list list)381 syssegflush(Ar0* ar0, va_list list)
382 {
383 	Segment *s;
384 	uintptr addr, pgsize;
385 	Pte *pte;
386 	usize chunk, l, len, pe, ps;
387 
388 	/*
389 	 * int segflush(void*, ulong);
390 	 * should be
391 	 * int segflush(void*, usize);
392 	 */
393 	addr = PTR2UINT(va_arg(list, void*));
394 	len = va_arg(list, usize);
395 
396 	while(len > 0) {
397 		s = seg(up, addr, 1);
398 		if(s == nil)
399 			error(Ebadarg);
400 
401 		s->flushme = 1;
402 		pgsize = segpgsize(s);
403 	more:
404 		l = len;
405 		if(addr+l > s->top)
406 			l = s->top - addr;
407 
408 		ps = addr-s->base;
409 		pte = s->map[ps/s->ptemapmem];
410 		ps &= s->ptemapmem-1;
411 		pe = s->ptemapmem;
412 		if(pe-ps > l){
413 			pe = ps + l;
414 			pe = (pe+pgsize-1)&~(pgsize-1);
415 		}
416 		if(pe == ps) {
417 			qunlock(&s->lk);
418 			error(Ebadarg);
419 		}
420 
421 		if(pte)
422 			pteflush(pte, ps/pgsize, pe/pgsize);
423 
424 		chunk = pe-ps;
425 		len -= chunk;
426 		addr += chunk;
427 
428 		if(len > 0 && addr < s->top)
429 			goto more;
430 
431 		qunlock(&s->lk);
432 	}
433 	mmuflush();
434 
435 	ar0->i = 0;
436 }
437