248 lines
6.5 KiB
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;
|
|
}
|