jehanne/sys/src/kern/port/umem/raw.c

248 lines
6.5 KiB
C

/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/umem/internals.h"
/* Memory areas can be
*
* - SgPhysical: for example a DMA area, a framebuffer or a
* fixed segment created by devsegment
*
* - SgShared: as "shared" or any segment created by devsegment
*
* - SgBSS: as "memory" or another segment created
* with SgBSS by segment attach
*
* Collectively, we call areas of type SgShared and SgBSS "virtual"
* as they are not identified by a physical address (pa).
* We also keep them at the end of the memory_areas array, sorted by
* name, so that rawmem_find can use a binary search to find them.
*
* SgPhysical areas are instead kept at the beginning of the
* memory_areas array, sorted by physical address, so that a binary
* search will be the fastest way to find the matching one in rawmem_lookup
*/
static RawMemory* memory_areas;
static int areas_allocated;
static int areas_used;
static int virtual_areas;
static RWlock areas_lock;
void
rawmem_init(void)
{
areas_allocated = 8;
memory_areas = jehanne_malloc(areas_allocated*sizeof(RawMemory));
if(memory_areas == nil)
panic("rawmem_init: out of memory");
rawmem_register("shared", 0, SgShared, -1);
rawmem_register("memory", 0, SgBSS, -1);
}
int
rawmem_register(char* name, uintptr_t pa, unsigned int attr, uintptr_t size)
{
uintptr_t opa, osize;
unsigned int oattr;
int used_areas;
RawMemory* tmp;
if(name == nil)
panic("rawmem_register: nil name, pc %#p", getcallerpc());
if(attr == 0)
panic("rawmem_register: zero attr, pc %#p", getcallerpc());
if(size == 0)
panic("rawmem_register: zero size, pc %#p", getcallerpc()); /* any valid use case? */
switch(attr&SegmentTypesMask){
case SgPhysical:
if(pa == 0)
panic("rawmem_register: zero pa on Physical memory area, pc %#p", getcallerpc());
break;
case SgShared:
case SgBSS:
if(pa != 0)
panic("rawmem_register: zero pa on Virtual memory area, pc %#p", getcallerpc());
break;
case SgLoad:
case SgStack:
panic("rawmem_register: %s segment not allowed, pc %#p", segment_types[attr&SegmentTypesMask], getcallerpc());
default:
panic("rawmem_register: unknown segment type %uxd, pc %#p", attr&SegmentTypesMask, getcallerpc());
}
used_areas = areas_used;
LookupArea:
if(rawmem_find(&name, &opa, &oattr, &osize))
if(opa == pa && oattr == attr && osize == size)
return 1; /* save work */
else
panic("rawmem_register: '%s' already registered, pc %#p", getcallerpc());
wlock(&areas_lock);
if(used_areas != areas_used){
/* if any area has been added while waiting the wlock,
* repeat the search
*/
used_areas = areas_used;
wunlock(&areas_lock);
goto LookupArea;
}
if(areas_used == areas_allocated){
areas_allocated += 8;
tmp = jehanne_realloc(memory_areas, areas_allocated*sizeof(RawMemory));
if(tmp == nil)
goto RegisterFailed;
memory_areas = tmp;
}
if(pa == 0){
tmp = memory_areas+areas_used-virtual_areas;
while(tmp < memory_areas+areas_used){
if(jehanne_strcmp(tmp->name, name) > 0)
break;
++tmp;
}
jehanne_memmove(tmp+1, tmp, sizeof(RawMemory)*(memory_areas+areas_used-tmp));
++virtual_areas;
} else {
tmp = memory_areas;
while(tmp < memory_areas+areas_used-virtual_areas){
if(tmp->pa > pa){
if(pa + size > tmp->pa)
panic("rawmem_register: '%s' overlaps with '%s', pc %#p", name, tmp->name, getcallerpc());
break;
}
if(tmp->pa + tmp->size > pa)
panic("rawmem_register: '%s' overlaps with '%s', pc %#p", name, tmp->name, getcallerpc());
++tmp;
}
jehanne_memmove(tmp+1, tmp, sizeof(RawMemory)*(memory_areas+areas_used-tmp));
}
tmp->name = name; /* TODO: ensure this is a readonly constant */
tmp->pa = pa;
tmp->attr = attr;
tmp->size = size;
++areas_used;
wunlock(&areas_lock);
return 1;
RegisterFailed:
wunlock(&areas_lock);
return 0;
}
int
rawmem_find(char** name, uintptr_t *pa, unsigned int *attr, uintptr_t *size)
{
int b, t, i, c;
RawMemory* result;
if(name == nil)
panic("rawmem_find: nil name pointer, pc %#p", getcallerpc());
if(*name == nil)
panic("rawmem_find: nil name, pc %#p", getcallerpc());
result = nil;
/* try a binary search in virtual areas (memory_areas+virtual_areas)
* since they are sorted by name, and fallback to a for loop
* on physical areas (memory_areas[0 to used_areas - virtual_areas])
* when it find nothing.
*/
rlock(&areas_lock);
b = areas_used - virtual_areas;
t = areas_used;
while(result == nil && b < t)
{
i = b + ((t - b)/2);
if((c = jehanne_strcmp(memory_areas[i].name, *name)) == 0)
result = &memory_areas[i];
else if(c < 0)
b = i + 1;
else /* c > 0 */
t = i;
}
t = areas_used - virtual_areas;
for(i = 0; result == nil && i < t; ++i)
if(jehanne_strcmp(*name, memory_areas[i].name) == 0)
result = &memory_areas[i];
if(result != nil){
*name = memory_areas[i].name;
if(pa)
*pa = result->pa;
if(attr)
*attr = result->attr;
if(size)
*size = result->size;
}
runlock(&areas_lock);
return result != nil ? 1 : 0;
}
int
rawmem_lookup(uintptr_t addr, char** name, uintptr_t *pa, unsigned int *attr, uintptr_t *size)
{
int b, t, i;
long c;
RawMemory* result;
if(addr == 0)
panic("rawmem_lookup: zero addr, pc %#p", getcallerpc());
b = 0;
result = nil;
rlock(&areas_lock);
t = areas_used - virtual_areas;
while(result == nil && b < t) {
i = b + ((t - b)/2);
if((c = memory_areas[i].pa - addr) <= 0 && addr < memory_areas[i].pa + memory_areas[i].size )
result = &memory_areas[i];
else if(c < 0)
b = i + 1;
else /* c > 0 */
t = i;
}
if(result != nil){
if(name){
/* NOTE: we should create a copy of name, at least
* until we don't ensure that it is a string from a
* readonly page in the kernel image.
*/
*name = result->name;
}
if(pa)
*pa = result->pa;
if(attr)
*attr = result->attr;
if(size)
*size = result->size;
}
runlock(&areas_lock);
return result != nil ? 1 : 0;
}