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