aspace.c (10859B)
1 /* Copyright 1998-1999, Brian J. Swetland. All rights reserved. 2 ** Distributed under the terms of the OpenBLT License 3 */ 4 5 #include <blt/os.h> 6 #include "kernel.h" 7 #include "memory.h" 8 #include "aspace.h" 9 10 extern uint32 _cr3; 11 extern aspace_t *flat; 12 13 int snprintf (char *s, int len, const char *fmt, ...); 14 15 void aspace_pprint(uint32 *page, uint32 vbase) 16 { 17 int i,j; 18 19 char buf[80]; 20 21 for(i=0;i<1024;i++){ 22 if(page[i]){ 23 j = page[i]; 24 snprintf(buf,80,"%s %s %s ", 25 (j & 4) ? "USR" : "SYS", 26 (j & 2) ? "RW" : "RO", 27 (j & 1) ? "P" : "X"); 28 29 snprintf(buf+9,80-9,"%xv %xp", i*4096+vbase, j&0xFFFFFF00); 30 31 for(j=1;(i+j < 1024) && 32 ( (0xFFFFFF00 & page[i+j]) == 33 (0xFFFFFF00 & page[i+j-1])+0x1000 );j++); 34 35 if(j>1) { 36 i += j-1; 37 snprintf(buf+29,80-29," - %xp (%x)", 38 0x0FFF + (page[i]&0xFFFFFF00), j); 39 } 40 kprintf(buf); 41 } 42 } 43 } 44 45 void aspace_kprint(aspace_t *a) 46 { 47 aspace_pprint(a->high,0x80000000); 48 } 49 50 void aspace_print(aspace_t *a) 51 { 52 aspace_pprint(a->ptab,0); 53 } 54 55 aspace_t *aspace_create(void) 56 { 57 int i; 58 uint32 phys0,phys1; 59 uint32 *raw0,*raw1; 60 61 aspace_t *a = kmalloc(aspace_t); 62 raw0 = kgetpage(&phys0); 63 raw1 = kgetpage(&phys1); 64 a->pdir = raw0; 65 a->ptab = raw1; 66 a->high = flat->high; 67 a->pdirphys = phys0; 68 list_init(&a->areas); 69 70 for(i=0;i<1024;i++){ 71 a->pdir[i] = 0; 72 a->ptab[i] = 0; 73 } 74 a->pdir[0] = phys1 | 7; 75 a->pdir[512] = (_cr3 + 2*4096) | 3; 76 rsrc_bind(&a->rsrc, RSRC_ASPACE, kernel_team); 77 return a; 78 } 79 80 void aspace_protect(aspace_t *a, uint32 virt, uint32 flags) 81 { 82 a->ptab[virt] = ((a->ptab[virt] & 0xFFFFF000) | (flags & 0x00000FFF)); 83 } 84 85 void aspace_map(aspace_t *a, uint32 phys, uint32 virt, uint32 len, uint32 flags) 86 { 87 int i; 88 for(i=0;i<len;i++){ 89 a->ptab[virt+i] = (((phys+i)*4096) & 0xFFFFF000) | 90 (flags & 0x00000FFF); 91 local_flush_pte(0x1000*((virt)+i)); 92 } 93 } 94 95 void aspace_maphi(aspace_t *a, uint32 phys, uint32 virt, uint32 len, uint32 flags) 96 { 97 int i; 98 for(i=0;i<len;i++){ 99 a->high[(virt&0x3FF)+i] = (phys+i)*4096 | flags; 100 local_flush_pte(0x1000*((virt)+i)); 101 } 102 103 } 104 105 void aspace_clr(aspace_t *a, uint32 virt, uint32 len) 106 { 107 int i; 108 for(i=0;i<len;i++){ 109 a->ptab[virt+i] = 0; 110 } 111 } 112 113 void aspace_destroy(aspace_t *a) 114 { 115 area_t *area; 116 117 /* tear down all attached areas */ 118 while((area = (area_t*) list_peek_head(&a->areas))){ 119 area_destroy(a, area->rsrc.id); 120 } 121 122 rsrc_release(&a->rsrc); 123 /* release page directory and table(s) */ 124 kfreepage(a->pdir); 125 kfreepage(a->ptab); 126 kfree(aspace_t, a); 127 } 128 129 /* find a span of contig virtual pages */ 130 static int locate_span(aspace_t *a, uint32 start, uint32 len) 131 { 132 uint32 *map = a->ptab; 133 uint32 found = 0; 134 uint32 foundat = 0; 135 136 /* default to 2mb line if unspec or invalid */ 137 if((start < 1) || (start > 1023)) start = 512; 138 139 while((found < len) && (start < 1024)){ 140 if(map[start]){ 141 found = 0; 142 foundat = 0; 143 } else { 144 if(found == 0) foundat = start; 145 found++; 146 } 147 start++; 148 } 149 return foundat; 150 } 151 152 /* userland calls */ 153 int area_create(aspace_t *aspace, off_t size, off_t virt, void **addr, uint32 flags) 154 { 155 int ppo,i,p=0,at; 156 area_t *area; 157 pagegroup_t *pg; 158 phys_page_t *pp; 159 size = (size & 0x0FFF) ? (size / 0x1000 + 1) : (size / 0x1000); 160 161 if(size < 1) size = 1; 162 163 if(flags & AREA_PHYSMAP) { 164 p = ((uint32) *addr) / 0x1000; 165 } 166 167 /* find a virtaddr */ 168 if(!(at = locate_span(aspace, virt/0x1000, size))){ 169 return ERR_MEMORY; 170 } 171 172 /* create a fresh pagegroup and enough phys_page_t's */ 173 pg = (pagegroup_t *) kmalloc(pagegroup_t); 174 pg->flags = flags; 175 pg->refcount = 1; 176 pg->size = size; 177 pg->pages = NULL; 178 list_init(&pg->areas); 179 for(i=0;i<size;i+=6){ 180 pp = (phys_page_t *) kmalloc(phys_page_t); 181 pp->refcount = 0; 182 pp->next = pg->pages; 183 pg->pages = pp; 184 }; 185 186 /* create an area to ref the pagegroup */ 187 area = (area_t *) kmalloc(area_t); 188 area->pgroup = pg; 189 area->virt_addr = at; 190 area->length = size; 191 area->maxlength = size; 192 193 /* link this area into the new pagegroup */ 194 list_add_tail(&pg->areas, area); 195 196 /* link this area into the aspace's arealist */ 197 list_add_tail(&aspace->areas, area); 198 199 /* check for valid ptr */ 200 *addr = (void *) (at * 0x1000); 201 202 /* allocate pages, fill phys_page_t's, and map 'em */ 203 for(ppo=0,pp=pg->pages,i=0;i<size;i++){ 204 if(!(flags & AREA_PHYSMAP)) { 205 p = getpage(); 206 } 207 aspace_map(aspace, p, at, 1, 7); 208 pp->addr[ppo] = p; 209 ppo++; 210 if(ppo == 6) { 211 ppo = 0; 212 pp = pp->next; 213 } 214 if(flags & AREA_PHYSMAP) p++; 215 at++; 216 } 217 218 /* 219 * zero extra space in last phys_page_t. 220 * careful, ppo == 0 and pp == NULL if size == 6. 221 */ 222 while((ppo < 6) && (pp != NULL)){ 223 pp->addr[ppo] = 0; 224 ppo++; 225 } 226 227 /* bind the resource and return the id */ 228 rsrc_bind(&(area->rsrc), RSRC_AREA, current->rsrc.owner); 229 return area->rsrc.id; 230 } 231 232 int area_create_uber(off_t size, void *addr) 233 { 234 int ppo,i,p,at=0; 235 area_t *area; 236 pagegroup_t *pg; 237 phys_page_t *pp; 238 239 size = (size & 0x0FFF) ? (size / 0x1000 + 1) : (size / 0x1000); 240 if(size < 1) size = 1; 241 242 p = ((uint32) addr) / 0x1000; 243 244 /* create a fresh pagegroup and enough phys_page_t's */ 245 pg = (pagegroup_t *) kmalloc(pagegroup_t); 246 pg->flags = AREA_PHYSMAP; 247 pg->refcount = 1; 248 pg->size = size; 249 pg->pages = NULL; 250 list_init(&pg->areas); 251 for(i=0;i<size;i+=6){ 252 pp = (phys_page_t *) kmalloc(phys_page_t); 253 pp->refcount = 0; 254 pp->next = pg->pages; 255 pg->pages = pp; 256 }; 257 258 /* create an area to ref the pagegroup */ 259 area = (area_t *) kmalloc(area_t); 260 area->pgroup = pg; 261 area->virt_addr = at; 262 area->length = size; 263 area->maxlength = size; 264 265 /* link this area into the new pagegroup */ 266 list_add_tail(&pg->areas, area); 267 268 /* allocate pages, fill phys_page_t's, and map 'em */ 269 for(ppo=0,pp=pg->pages,i=0;i<size;i++){ 270 pp->addr[ppo] = p; 271 ppo++; 272 if(ppo == 6) { 273 ppo = 0; 274 pp = pp->next; 275 } 276 p++; 277 at++; 278 } 279 280 /* 281 * zero extra space in last phys_page_t. 282 * careful, ppo == 0 and pp == NULL if size == 6. 283 */ 284 while((ppo < 6) && (pp != NULL)){ 285 pp->addr[ppo] = 0; 286 ppo++; 287 } 288 289 /* bind the resource and return the id */ 290 rsrc_bind(&(area->rsrc), RSRC_AREA, kernel_team); 291 return area->rsrc.id; 292 } 293 294 int area_clone(aspace_t *aspace, int area_id, off_t virt, void **addr, uint32 flags) 295 { 296 area_t *area0, *area1; 297 uint32 at, size; 298 phys_page_t *pp; 299 int i,ppo; 300 301 /* make sure the area exists */ 302 /* xxx : check perm */ 303 if(!(area0 = rsrc_find_area(area_id))) return ERR_RESOURCE; 304 305 /* find virt space for it */ 306 if(!(at = locate_span(aspace, virt/0x1000, area0->length))){ 307 return ERR_MEMORY; 308 } 309 310 311 /* xxx lock area1 and area1->pgroup */ 312 313 /* allocate the clone area and init it from the orig */ 314 area1 = (area_t *) kmalloc(area_t); 315 area0->pgroup->refcount++; 316 area1->pgroup = area0->pgroup; 317 size = area1->length = area0->length; 318 area1->maxlength = area0->maxlength; 319 area1->virt_addr = at; 320 321 /* link this area into the new pagegroup */ 322 list_add_tail(&area0->pgroup->areas, area1); 323 324 /* link this area into the aspace's arealist */ 325 list_add_tail(&aspace->areas, area1); 326 327 /* check for valid ptr */ 328 *addr = (void *) (at * 0x1000); 329 330 /* map the phys_page_t's into the clone area */ 331 for(ppo=0,i=0,pp=area1->pgroup->pages;i<size;i++){ 332 aspace_map(aspace, pp->addr[ppo], at, 1, 7); 333 at++; 334 ppo++; 335 if(ppo == 6){ 336 ppo = 0; 337 pp = pp->next; 338 } 339 } 340 /* 341 kprintf("area0 pgroup %x",area1->pgroup); 342 kprintf("area1 pgroup %x",area1->pgroup); 343 */ 344 /* bind and return an id */ 345 rsrc_bind(&(area1->rsrc), RSRC_AREA, current->rsrc.owner); 346 // kprintf("area_clone(%d) -> %d",area_id,area1->rsrc.id); 347 return area1->rsrc.id; 348 } 349 350 int area_destroy(aspace_t *aspace, int area_id) 351 { 352 area_t *area; 353 pagegroup_t *pg; 354 355 if(!(area = rsrc_find_area(area_id))) return ERR_RESOURCE; 356 357 /* find and unchain the area from its aspace -- complain if it is foreign */ 358 if(list_remove(&aspace->areas, area)) return ERR_RESOURCE; 359 360 /* unmap the memory */ 361 aspace_clr(aspace, area->virt_addr, area->length); 362 363 pg = area->pgroup; 364 365 /* remove this area from the pgroup's area list */ 366 list_remove(&pg->areas, area); 367 368 /* decr the pagegroup refcount and tear it down if zero */ 369 pg->refcount--; 370 371 if(pg->refcount == 0){ 372 int release = !(pg->flags & AREA_PHYSMAP); 373 int count = 0; 374 phys_page_t *pp; 375 376 while((pp = pg->pages)){ 377 pg->pages = pp->next; 378 if(release){ 379 for(count=0;pg->size && (count < 6);count++){ 380 putpage(pp->addr[count]); 381 pg->size--; 382 } 383 } 384 kfree(phys_page_t, pp); 385 } 386 kfree(pagegroup_t, pg); 387 } 388 389 rsrc_release(&area->rsrc); 390 391 kfree(area_t, area); 392 return ERR_NONE; 393 } 394 395 int area_resize(aspace_t *aspace, int area_id, off_t size) 396 { 397 area_t *area; 398 pagegroup_t *pg; 399 int ppo; 400 phys_page_t *pp,*pp0; 401 uint32 at,p; 402 403 if(!(area = rsrc_find_area(area_id))) return ERR_RESOURCE; 404 405 pg = area->pgroup; 406 pp = area->pgroup->pages; 407 408 // kprintf("area_resize(%x,%d,%d)",aspace,area_id,size); 409 410 if(size <= pg->size*0x1000) return 0; 411 412 size = size/0x1000 - pg->size; /* pages to add */ 413 414 // kprintf("area_resize: %x %x %x + %x",area,pg,pp,size); 415 416 at = locate_span(aspace, area->virt_addr + pg->size, size); 417 if(at != (area->virt_addr + pg->size)) { 418 kprintf("oops: cannot grow area (%x != %x)",at,area->virt_addr+pg->size); 419 return ERR_MEMORY; 420 } 421 422 while(pp->next) pp = pp->next; 423 ppo = pg->size % 6; 424 425 pg->size += size; 426 area->length += size; 427 area->maxlength += size; 428 429 while(size){ 430 if(!ppo){ 431 pp0 = (phys_page_t *) kmalloc(phys_page_t); 432 pp0->next = NULL; 433 pp->next = pp0; 434 pp0->refcount = 0; 435 pp = pp0; 436 } 437 p = getpage(); 438 aspace_map(aspace, p, at, 1, 7); 439 pp->addr[ppo] = p; 440 at++; 441 ppo++; 442 if(ppo == 6) ppo = 0; 443 size--; 444 } 445 446 return 0; 447 } 448 449 450 451 452 453