/* * This file is part of Jehanne. * * Copyright (C) 2016 Giacomo Tesio * * 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 . */ #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; }