openblt

a hobby OS from the late 90s
git clone http://frotz.net/git/openblt.git
Log | Files | Refs | LICENSE

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