Made VMAddressSpace an abstract base class and moved the area management into
new derived classes VM{Kernel,User}AddressSpace. Currently those are
identical, but that will change.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34492 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -23,8 +23,8 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
VMAddressSpace(team_id id, addr_t base,
|
VMAddressSpace(team_id id, addr_t base,
|
||||||
size_t size, bool kernel);
|
size_t size, const char* name);
|
||||||
~VMAddressSpace();
|
virtual ~VMAddressSpace();
|
||||||
|
|
||||||
static status_t Init();
|
static status_t Init();
|
||||||
static status_t InitPostSem();
|
static status_t InitPostSem();
|
||||||
@@ -58,27 +58,32 @@ public:
|
|||||||
void IncrementChangeCount()
|
void IncrementChangeCount()
|
||||||
{ fChangeCount++; }
|
{ fChangeCount++; }
|
||||||
|
|
||||||
VMArea* FirstArea() const;
|
|
||||||
|
|
||||||
VMArea* LookupArea(addr_t address) const;
|
|
||||||
status_t InsertArea(void** _address, uint32 addressSpec,
|
|
||||||
addr_t size, VMArea* area);
|
|
||||||
void RemoveArea(VMArea* area);
|
|
||||||
|
|
||||||
bool CanResizeArea(VMArea* area, size_t newSize);
|
|
||||||
status_t ResizeArea(VMArea* area, size_t newSize);
|
|
||||||
status_t ResizeAreaHead(VMArea* area, size_t size);
|
|
||||||
status_t ResizeAreaTail(VMArea* area, size_t size);
|
|
||||||
|
|
||||||
status_t ReserveAddressRange(void** _address,
|
|
||||||
uint32 addressSpec, size_t size,
|
|
||||||
uint32 flags);
|
|
||||||
status_t UnreserveAddressRange(addr_t address,
|
|
||||||
size_t size);
|
|
||||||
void UnreserveAllAddressRanges();
|
|
||||||
|
|
||||||
inline AreaIterator GetAreaIterator();
|
inline AreaIterator GetAreaIterator();
|
||||||
|
|
||||||
|
VMAddressSpace*& HashTableLink() { return fHashTableLink; }
|
||||||
|
|
||||||
|
virtual VMArea* FirstArea() const = 0;
|
||||||
|
virtual VMArea* NextArea(VMArea* area) const = 0;
|
||||||
|
|
||||||
|
virtual VMArea* LookupArea(addr_t address) const = 0;
|
||||||
|
virtual status_t InsertArea(void** _address, uint32 addressSpec,
|
||||||
|
addr_t size, VMArea* area) = 0;
|
||||||
|
virtual void RemoveArea(VMArea* area) = 0;
|
||||||
|
|
||||||
|
virtual bool CanResizeArea(VMArea* area, size_t newSize) = 0;
|
||||||
|
virtual status_t ResizeArea(VMArea* area, size_t newSize) = 0;
|
||||||
|
virtual status_t ResizeAreaHead(VMArea* area, size_t size) = 0;
|
||||||
|
virtual status_t ResizeAreaTail(VMArea* area, size_t size) = 0;
|
||||||
|
|
||||||
|
virtual status_t ReserveAddressRange(void** _address,
|
||||||
|
uint32 addressSpec, size_t size,
|
||||||
|
uint32 flags) = 0;
|
||||||
|
virtual status_t UnreserveAddressRange(addr_t address,
|
||||||
|
size_t size) = 0;
|
||||||
|
virtual void UnreserveAllAddressRanges() = 0;
|
||||||
|
|
||||||
|
virtual void Dump() const;
|
||||||
|
|
||||||
static status_t Create(team_id teamID, addr_t base, size_t size,
|
static status_t Create(team_id teamID, addr_t base, size_t size,
|
||||||
bool kernel,
|
bool kernel,
|
||||||
VMAddressSpace** _addressSpace);
|
VMAddressSpace** _addressSpace);
|
||||||
@@ -94,26 +99,14 @@ public:
|
|||||||
|
|
||||||
static VMAddressSpace* Get(team_id teamID);
|
static VMAddressSpace* Get(team_id teamID);
|
||||||
|
|
||||||
VMAddressSpace*& HashTableLink() { return fHashTableLink; }
|
protected:
|
||||||
|
|
||||||
void Dump() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
status_t _InsertAreaIntoReservedRegion(addr_t start,
|
|
||||||
size_t size, VMArea* area);
|
|
||||||
status_t _InsertAreaSlot(addr_t start, addr_t size,
|
|
||||||
addr_t end, uint32 addressSpec,
|
|
||||||
VMArea* area);
|
|
||||||
|
|
||||||
static int _DumpCommand(int argc, char** argv);
|
static int _DumpCommand(int argc, char** argv);
|
||||||
static int _DumpListCommand(int argc, char** argv);
|
static int _DumpListCommand(int argc, char** argv);
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
friend class Iterator;
|
|
||||||
|
|
||||||
struct HashDefinition;
|
struct HashDefinition;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
VMAddressSpace* fHashTableLink;
|
VMAddressSpace* fHashTableLink;
|
||||||
addr_t fBase;
|
addr_t fBase;
|
||||||
addr_t fEndAddress; // base + (size - 1)
|
addr_t fEndAddress; // base + (size - 1)
|
||||||
@@ -124,8 +117,6 @@ private:
|
|||||||
int32 fFaultCount;
|
int32 fFaultCount;
|
||||||
int32 fChangeCount;
|
int32 fChangeCount;
|
||||||
vm_translation_map fTranslationMap;
|
vm_translation_map fTranslationMap;
|
||||||
VMAddressSpaceAreaList fAreas;
|
|
||||||
mutable VMArea* fAreaHint;
|
|
||||||
bool fDeleting;
|
bool fDeleting;
|
||||||
static VMAddressSpace* sKernelAddressSpace;
|
static VMAddressSpace* sKernelAddressSpace;
|
||||||
};
|
};
|
||||||
@@ -139,9 +130,9 @@ public:
|
|||||||
|
|
||||||
AreaIterator(VMAddressSpace* addressSpace)
|
AreaIterator(VMAddressSpace* addressSpace)
|
||||||
:
|
:
|
||||||
fIterator(addressSpace->fAreas.GetIterator())
|
fAddressSpace(addressSpace),
|
||||||
|
fNext(addressSpace->FirstArea())
|
||||||
{
|
{
|
||||||
_SkipReserved();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasNext() const
|
bool HasNext() const
|
||||||
@@ -152,40 +143,21 @@ public:
|
|||||||
VMArea* Next()
|
VMArea* Next()
|
||||||
{
|
{
|
||||||
VMArea* result = fNext;
|
VMArea* result = fNext;
|
||||||
_SkipReserved();
|
fNext = fAddressSpace->NextArea(fNext);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rewind()
|
void Rewind()
|
||||||
{
|
{
|
||||||
fIterator.Rewind();
|
fNext = fAddressSpace->FirstArea();
|
||||||
_SkipReserved();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _SkipReserved()
|
VMAddressSpace* fAddressSpace;
|
||||||
{
|
VMArea* fNext;
|
||||||
while ((fNext = fIterator.Next()) != NULL
|
|
||||||
&& fNext->id == RESERVED_AREA_ID) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
VMAddressSpaceAreaList::Iterator fIterator;
|
|
||||||
VMArea* fNext;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
inline VMArea*
|
|
||||||
VMAddressSpace::FirstArea() const
|
|
||||||
{
|
|
||||||
VMArea* area = fAreas.Head();
|
|
||||||
while (area != NULL && area->id == RESERVED_AREA_ID)
|
|
||||||
area = fAreas.GetNext(area);
|
|
||||||
return area;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline VMAddressSpace::AreaIterator
|
inline VMAddressSpace::AreaIterator
|
||||||
VMAddressSpace::GetAreaIterator()
|
VMAddressSpace::GetAreaIterator()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
struct VMAddressSpace;
|
struct VMAddressSpace;
|
||||||
struct VMCache;
|
struct VMCache;
|
||||||
|
struct VMKernelAddressSpace;
|
||||||
|
struct VMUserAddressSpace;
|
||||||
|
|
||||||
|
|
||||||
struct VMArea {
|
struct VMArea {
|
||||||
@@ -59,6 +61,8 @@ struct VMArea {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
friend class VMAddressSpace;
|
friend class VMAddressSpace;
|
||||||
|
friend class VMKernelAddressSpace;
|
||||||
|
friend class VMUserAddressSpace;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetBase(addr_t base) { fBase = base; }
|
void SetBase(addr_t base) { fBase = base; }
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ KernelMergeObject kernel_vm.o :
|
|||||||
VMArea.cpp
|
VMArea.cpp
|
||||||
VMCache.cpp
|
VMCache.cpp
|
||||||
VMDeviceCache.cpp
|
VMDeviceCache.cpp
|
||||||
|
VMKernelAddressSpace.cpp
|
||||||
VMNullCache.cpp
|
VMNullCache.cpp
|
||||||
#vm_tests.c
|
VMUserAddressSpace.cpp
|
||||||
|
|
||||||
: $(TARGET_KERNEL_PIC_CCFLAGS)
|
: $(TARGET_KERNEL_PIC_CCFLAGS)
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -21,6 +21,9 @@
|
|||||||
#include <vm/vm.h>
|
#include <vm/vm.h>
|
||||||
#include <vm/VMArea.h>
|
#include <vm/VMArea.h>
|
||||||
|
|
||||||
|
#include "VMKernelAddressSpace.h"
|
||||||
|
#include "VMUserAddressSpace.h"
|
||||||
|
|
||||||
|
|
||||||
//#define TRACE_VM
|
//#define TRACE_VM
|
||||||
#ifdef TRACE_VM
|
#ifdef TRACE_VM
|
||||||
@@ -33,17 +36,6 @@
|
|||||||
#define ASPACE_HASH_TABLE_SIZE 1024
|
#define ASPACE_HASH_TABLE_SIZE 1024
|
||||||
|
|
||||||
|
|
||||||
/*! Verifies that an area with the given aligned base and size fits into
|
|
||||||
the spot defined by base and limit and checks for overflows.
|
|
||||||
*/
|
|
||||||
static inline bool
|
|
||||||
is_valid_spot(addr_t base, addr_t alignedBase, addr_t size, addr_t limit)
|
|
||||||
{
|
|
||||||
return (alignedBase >= base && alignedBase + (size - 1) > alignedBase
|
|
||||||
&& alignedBase + (size - 1) <= limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - AddressSpaceHashDefinition
|
// #pragma mark - AddressSpaceHashDefinition
|
||||||
|
|
||||||
|
|
||||||
@@ -84,7 +76,7 @@ VMAddressSpace* VMAddressSpace::sKernelAddressSpace;
|
|||||||
|
|
||||||
|
|
||||||
VMAddressSpace::VMAddressSpace(team_id id, addr_t base, size_t size,
|
VMAddressSpace::VMAddressSpace(team_id id, addr_t base, size_t size,
|
||||||
bool kernel)
|
const char* name)
|
||||||
:
|
:
|
||||||
fBase(base),
|
fBase(base),
|
||||||
fEndAddress(base + (size - 1)),
|
fEndAddress(base + (size - 1)),
|
||||||
@@ -93,18 +85,15 @@ VMAddressSpace::VMAddressSpace(team_id id, addr_t base, size_t size,
|
|||||||
fRefCount(1),
|
fRefCount(1),
|
||||||
fFaultCount(0),
|
fFaultCount(0),
|
||||||
fChangeCount(0),
|
fChangeCount(0),
|
||||||
fAreaHint(NULL),
|
|
||||||
fDeleting(false)
|
fDeleting(false)
|
||||||
{
|
{
|
||||||
rw_lock_init(&fLock, kernel ? "kernel address space" : "address space");
|
rw_lock_init(&fLock, name);
|
||||||
|
// rw_lock_init(&fLock, kernel ? "kernel address space" : "address space");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VMAddressSpace::~VMAddressSpace()
|
VMAddressSpace::~VMAddressSpace()
|
||||||
{
|
{
|
||||||
if (this == sKernelAddressSpace)
|
|
||||||
panic("deleting the kernel aspace!\n");
|
|
||||||
|
|
||||||
TRACE(("VMAddressSpace::~VMAddressSpace: called on aspace %" B_PRId32 "\n",
|
TRACE(("VMAddressSpace::~VMAddressSpace: called on aspace %" B_PRId32 "\n",
|
||||||
ID()));
|
ID()));
|
||||||
|
|
||||||
@@ -192,12 +181,27 @@ VMAddressSpace::RemoveAndPut()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
VMAddressSpace::Dump() const
|
||||||
|
{
|
||||||
|
kprintf("dump of address space at %p:\n", this);
|
||||||
|
kprintf("id: 0x%lx\n", fID);
|
||||||
|
kprintf("ref_count: %ld\n", fRefCount);
|
||||||
|
kprintf("fault_count: %ld\n", fFaultCount);
|
||||||
|
kprintf("translation_map: %p\n", &fTranslationMap);
|
||||||
|
kprintf("base: 0x%lx\n", fBase);
|
||||||
|
kprintf("end: 0x%lx\n", fEndAddress);
|
||||||
|
kprintf("change_count: 0x%lx\n", fChangeCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*static*/ status_t
|
/*static*/ status_t
|
||||||
VMAddressSpace::Create(team_id teamID, addr_t base, size_t size, bool kernel,
|
VMAddressSpace::Create(team_id teamID, addr_t base, size_t size, bool kernel,
|
||||||
VMAddressSpace** _addressSpace)
|
VMAddressSpace** _addressSpace)
|
||||||
{
|
{
|
||||||
VMAddressSpace* addressSpace = new(nogrow) VMAddressSpace(teamID, base,
|
VMAddressSpace* addressSpace = kernel
|
||||||
size, kernel);
|
? (VMAddressSpace*)new(nogrow) VMKernelAddressSpace(teamID, base, size)
|
||||||
|
: (VMAddressSpace*)new(nogrow) VMUserAddressSpace(teamID, base, size);
|
||||||
if (addressSpace == NULL)
|
if (addressSpace == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
@@ -273,585 +277,6 @@ VMAddressSpace::Get(team_id teamID)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//! You must hold the address space's read lock.
|
|
||||||
VMArea*
|
|
||||||
VMAddressSpace::LookupArea(addr_t address) const
|
|
||||||
{
|
|
||||||
// check the area hint first
|
|
||||||
if (fAreaHint != NULL && fAreaHint->ContainsAddress(address))
|
|
||||||
return fAreaHint;
|
|
||||||
|
|
||||||
for (VMAddressSpaceAreaList::ConstIterator it = fAreas.GetIterator();
|
|
||||||
VMArea* area = it.Next();) {
|
|
||||||
if (area->id == RESERVED_AREA_ID)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (area->ContainsAddress(address)) {
|
|
||||||
fAreaHint = area;
|
|
||||||
return area;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! This inserts the area you pass into the address space.
|
|
||||||
It will also set the "_address" argument to its base address when
|
|
||||||
the call succeeds.
|
|
||||||
You need to hold the VMAddressSpace write lock.
|
|
||||||
*/
|
|
||||||
status_t
|
|
||||||
VMAddressSpace::InsertArea(void** _address, uint32 addressSpec, addr_t size,
|
|
||||||
VMArea* area)
|
|
||||||
{
|
|
||||||
addr_t searchBase, searchEnd;
|
|
||||||
status_t status;
|
|
||||||
|
|
||||||
switch (addressSpec) {
|
|
||||||
case B_EXACT_ADDRESS:
|
|
||||||
searchBase = (addr_t)*_address;
|
|
||||||
searchEnd = (addr_t)*_address + (size - 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case B_BASE_ADDRESS:
|
|
||||||
searchBase = (addr_t)*_address;
|
|
||||||
searchEnd = fEndAddress;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case B_ANY_ADDRESS:
|
|
||||||
case B_ANY_KERNEL_ADDRESS:
|
|
||||||
case B_ANY_KERNEL_BLOCK_ADDRESS:
|
|
||||||
searchBase = fBase;
|
|
||||||
// TODO: remove this again when vm86 mode is moved into the kernel
|
|
||||||
// completely (currently needs a userland address space!)
|
|
||||||
if (searchBase == USER_BASE)
|
|
||||||
searchBase = USER_BASE_ANY;
|
|
||||||
searchEnd = fEndAddress;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = _InsertAreaSlot(searchBase, size, searchEnd, addressSpec, area);
|
|
||||||
if (status == B_OK) {
|
|
||||||
*_address = (void*)area->Base();
|
|
||||||
fFreeSpace -= area->Size();
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//! You must hold the address space's write lock.
|
|
||||||
void
|
|
||||||
VMAddressSpace::RemoveArea(VMArea* area)
|
|
||||||
{
|
|
||||||
fAreas.Remove(area);
|
|
||||||
|
|
||||||
if (area->id != RESERVED_AREA_ID) {
|
|
||||||
IncrementChangeCount();
|
|
||||||
fFreeSpace += area->Size();
|
|
||||||
|
|
||||||
if (area == fAreaHint)
|
|
||||||
fAreaHint = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
VMAddressSpace::CanResizeArea(VMArea* area, size_t newSize)
|
|
||||||
{
|
|
||||||
VMArea* next = fAreas.GetNext(area);
|
|
||||||
addr_t newEnd = area->Base() + (newSize - 1);
|
|
||||||
if (next == NULL) {
|
|
||||||
if (fEndAddress >= newEnd)
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if (next->Base() > newEnd)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the area was created inside a reserved area, it can
|
|
||||||
// also be resized in that area
|
|
||||||
// TODO: if there is free space after the reserved area, it could
|
|
||||||
// be used as well...
|
|
||||||
if (next->id == RESERVED_AREA_ID && next->cache_offset <= area->Base()
|
|
||||||
&& next->Base() + (next->Size() - 1) >= newEnd) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
VMAddressSpace::ResizeArea(VMArea* area, size_t newSize)
|
|
||||||
{
|
|
||||||
addr_t newEnd = area->Base() + (newSize - 1);
|
|
||||||
VMArea* next = fAreas.GetNext(area);
|
|
||||||
if (next != NULL && next->Base() <= newEnd) {
|
|
||||||
if (next->id != RESERVED_AREA_ID
|
|
||||||
|| next->cache_offset > area->Base()
|
|
||||||
|| next->Base() + (next->Size() - 1) < newEnd) {
|
|
||||||
panic("resize situation for area %p has changed although we "
|
|
||||||
"should have the address space lock", area);
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// resize reserved area
|
|
||||||
addr_t offset = area->Base() + newSize - next->Base();
|
|
||||||
if (next->Size() <= offset) {
|
|
||||||
RemoveArea(next);
|
|
||||||
free(next);
|
|
||||||
} else {
|
|
||||||
status_t error = ResizeAreaHead(next, next->Size() - offset);
|
|
||||||
if (error != B_OK)
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResizeAreaTail(area, newSize);
|
|
||||||
// TODO: In case of error we should undo the change to the reserved
|
|
||||||
// area.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
VMAddressSpace::ResizeAreaHead(VMArea* area, size_t size)
|
|
||||||
{
|
|
||||||
size_t oldSize = area->Size();
|
|
||||||
if (size == oldSize)
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
area->SetBase(area->Base() + oldSize - size);
|
|
||||||
area->SetSize(size);
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
VMAddressSpace::ResizeAreaTail(VMArea* area, size_t size)
|
|
||||||
{
|
|
||||||
size_t oldSize = area->Size();
|
|
||||||
if (size == oldSize)
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
area->SetSize(size);
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
VMAddressSpace::ReserveAddressRange(void** _address, uint32 addressSpec,
|
|
||||||
size_t size, uint32 flags)
|
|
||||||
{
|
|
||||||
// check to see if this address space has entered DELETE state
|
|
||||||
if (fDeleting) {
|
|
||||||
// okay, someone is trying to delete this address space now, so we
|
|
||||||
// can't insert the area, let's back out
|
|
||||||
return B_BAD_TEAM_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
VMArea* area = VMArea::CreateReserved(this, flags);
|
|
||||||
if (area == NULL)
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
|
|
||||||
status_t status = InsertArea(_address, addressSpec, size, area);
|
|
||||||
if (status != B_OK) {
|
|
||||||
free(area);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
area->cache_offset = area->Base();
|
|
||||||
// we cache the original base address here
|
|
||||||
|
|
||||||
Get();
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
VMAddressSpace::UnreserveAddressRange(addr_t address, size_t size)
|
|
||||||
{
|
|
||||||
// check to see if this address space has entered DELETE state
|
|
||||||
if (fDeleting) {
|
|
||||||
// okay, someone is trying to delete this address space now, so we can't
|
|
||||||
// insert the area, so back out
|
|
||||||
return B_BAD_TEAM_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
// search area list and remove any matching reserved ranges
|
|
||||||
addr_t endAddress = address + (size - 1);
|
|
||||||
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
|
||||||
VMArea* area = it.Next();) {
|
|
||||||
// the area must be completely part of the reserved range
|
|
||||||
if (area->Base() + (area->Size() - 1) > endAddress)
|
|
||||||
break;
|
|
||||||
if (area->id == RESERVED_AREA_ID && area->Base() >= (addr_t)address) {
|
|
||||||
// remove reserved range
|
|
||||||
RemoveArea(area);
|
|
||||||
Put();
|
|
||||||
free(area);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
VMAddressSpace::UnreserveAllAddressRanges()
|
|
||||||
{
|
|
||||||
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
|
||||||
VMArea* area = it.Next();) {
|
|
||||||
if (area->id == RESERVED_AREA_ID) {
|
|
||||||
RemoveArea(area);
|
|
||||||
Put();
|
|
||||||
free(area);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
VMAddressSpace::Dump() const
|
|
||||||
{
|
|
||||||
kprintf("dump of address space at %p:\n", this);
|
|
||||||
kprintf("id: 0x%lx\n", fID);
|
|
||||||
kprintf("ref_count: %ld\n", fRefCount);
|
|
||||||
kprintf("fault_count: %ld\n", fFaultCount);
|
|
||||||
kprintf("translation_map: %p\n", &fTranslationMap);
|
|
||||||
kprintf("base: 0x%lx\n", fBase);
|
|
||||||
kprintf("end: 0x%lx\n", fEndAddress);
|
|
||||||
kprintf("change_count: 0x%lx\n", fChangeCount);
|
|
||||||
kprintf("area_hint: %p\n", fAreaHint);
|
|
||||||
|
|
||||||
kprintf("area_list:\n");
|
|
||||||
|
|
||||||
for (VMAddressSpaceAreaList::ConstIterator it = fAreas.GetIterator();
|
|
||||||
VMArea* area = it.Next();) {
|
|
||||||
kprintf(" area 0x%lx: ", area->id);
|
|
||||||
kprintf("base_addr = 0x%lx ", area->Base());
|
|
||||||
kprintf("size = 0x%lx ", area->Size());
|
|
||||||
kprintf("name = '%s' ", area->name);
|
|
||||||
kprintf("protection = 0x%lx\n", area->protection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Finds a reserved area that covers the region spanned by \a start and
|
|
||||||
\a size, inserts the \a area into that region and makes sure that
|
|
||||||
there are reserved regions for the remaining parts.
|
|
||||||
*/
|
|
||||||
status_t
|
|
||||||
VMAddressSpace::_InsertAreaIntoReservedRegion(addr_t start, size_t size,
|
|
||||||
VMArea* area)
|
|
||||||
{
|
|
||||||
VMArea* next;
|
|
||||||
|
|
||||||
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
|
||||||
(next = it.Next()) != NULL;) {
|
|
||||||
if (next->Base() <= start
|
|
||||||
&& next->Base() + (next->Size() - 1) >= start + (size - 1)) {
|
|
||||||
// This area covers the requested range
|
|
||||||
if (next->id != RESERVED_AREA_ID) {
|
|
||||||
// but it's not reserved space, it's a real area
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next == NULL)
|
|
||||||
return B_ENTRY_NOT_FOUND;
|
|
||||||
|
|
||||||
// Now we have to transfer the requested part of the reserved
|
|
||||||
// range to the new area - and remove, resize or split the old
|
|
||||||
// reserved area.
|
|
||||||
|
|
||||||
if (start == next->Base()) {
|
|
||||||
// the area starts at the beginning of the reserved range
|
|
||||||
fAreas.Insert(next, area);
|
|
||||||
|
|
||||||
if (size == next->Size()) {
|
|
||||||
// the new area fully covers the reversed range
|
|
||||||
fAreas.Remove(next);
|
|
||||||
Put();
|
|
||||||
free(next);
|
|
||||||
} else {
|
|
||||||
// resize the reserved range behind the area
|
|
||||||
next->SetBase(next->Base() + size);
|
|
||||||
next->SetSize(next->Size() - size);
|
|
||||||
}
|
|
||||||
} else if (start + size == next->Base() + next->Size()) {
|
|
||||||
// the area is at the end of the reserved range
|
|
||||||
fAreas.Insert(fAreas.GetNext(next), area);
|
|
||||||
|
|
||||||
// resize the reserved range before the area
|
|
||||||
next->SetSize(start - next->Base());
|
|
||||||
} else {
|
|
||||||
// the area splits the reserved range into two separate ones
|
|
||||||
// we need a new reserved area to cover this space
|
|
||||||
VMArea* reserved = VMArea::CreateReserved(this, next->protection);
|
|
||||||
if (reserved == NULL)
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
|
|
||||||
Get();
|
|
||||||
fAreas.Insert(fAreas.GetNext(next), reserved);
|
|
||||||
fAreas.Insert(reserved, area);
|
|
||||||
|
|
||||||
// resize regions
|
|
||||||
reserved->SetSize(next->Base() + next->Size() - start - size);
|
|
||||||
next->SetSize(start - next->Base());
|
|
||||||
reserved->SetBase(start + size);
|
|
||||||
reserved->cache_offset = next->cache_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
area->SetBase(start);
|
|
||||||
area->SetSize(size);
|
|
||||||
IncrementChangeCount();
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Must be called with this address space's write lock held */
|
|
||||||
status_t
|
|
||||||
VMAddressSpace::_InsertAreaSlot(addr_t start, addr_t size, addr_t end,
|
|
||||||
uint32 addressSpec, VMArea* area)
|
|
||||||
{
|
|
||||||
VMArea* last = NULL;
|
|
||||||
VMArea* next;
|
|
||||||
bool foundSpot = false;
|
|
||||||
|
|
||||||
TRACE(("VMAddressSpace::InsertAreaSlot: address space %p, start 0x%lx, "
|
|
||||||
"size %ld, end 0x%lx, addressSpec %ld, area %p\n", this, start,
|
|
||||||
size, end, addressSpec, area));
|
|
||||||
|
|
||||||
// do some sanity checking
|
|
||||||
if (start < fBase || size == 0 || end > fEndAddress
|
|
||||||
|| start + (size - 1) > end)
|
|
||||||
return B_BAD_ADDRESS;
|
|
||||||
|
|
||||||
if (addressSpec == B_EXACT_ADDRESS && area->id != RESERVED_AREA_ID) {
|
|
||||||
// search for a reserved area
|
|
||||||
status_t status = _InsertAreaIntoReservedRegion(start, size, area);
|
|
||||||
if (status == B_OK || status == B_BAD_VALUE)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
// There was no reserved area, and the slot doesn't seem to be used
|
|
||||||
// already
|
|
||||||
// TODO: this could be further optimized.
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t alignment = B_PAGE_SIZE;
|
|
||||||
if (addressSpec == B_ANY_KERNEL_BLOCK_ADDRESS) {
|
|
||||||
// align the memory to the next power of two of the size
|
|
||||||
while (alignment < size)
|
|
||||||
alignment <<= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
start = ROUNDUP(start, alignment);
|
|
||||||
|
|
||||||
// walk up to the spot where we should start searching
|
|
||||||
second_chance:
|
|
||||||
VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
|
||||||
while ((next = it.Next()) != NULL) {
|
|
||||||
if (next->Base() > start + (size - 1)) {
|
|
||||||
// we have a winner
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
last = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the right spot depending on the address specification - the area
|
|
||||||
// will be inserted directly after "last" ("next" is not referenced anymore)
|
|
||||||
|
|
||||||
switch (addressSpec) {
|
|
||||||
case B_ANY_ADDRESS:
|
|
||||||
case B_ANY_KERNEL_ADDRESS:
|
|
||||||
case B_ANY_KERNEL_BLOCK_ADDRESS:
|
|
||||||
{
|
|
||||||
// find a hole big enough for a new area
|
|
||||||
if (last == NULL) {
|
|
||||||
// see if we can build it at the beginning of the virtual map
|
|
||||||
addr_t alignedBase = ROUNDUP(fBase, alignment);
|
|
||||||
if (is_valid_spot(fBase, alignedBase, size,
|
|
||||||
next == NULL ? end : next->Base())) {
|
|
||||||
foundSpot = true;
|
|
||||||
area->SetBase(alignedBase);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
last = next;
|
|
||||||
next = it.Next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep walking
|
|
||||||
while (next != NULL) {
|
|
||||||
addr_t alignedBase = ROUNDUP(last->Base() + last->Size(),
|
|
||||||
alignment);
|
|
||||||
if (is_valid_spot(last->Base() + (last->Size() - 1),
|
|
||||||
alignedBase, size, next->Base())) {
|
|
||||||
foundSpot = true;
|
|
||||||
area->SetBase(alignedBase);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
last = next;
|
|
||||||
next = it.Next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundSpot)
|
|
||||||
break;
|
|
||||||
|
|
||||||
addr_t alignedBase = ROUNDUP(last->Base() + last->Size(),
|
|
||||||
alignment);
|
|
||||||
if (is_valid_spot(last->Base() + (last->Size() - 1), alignedBase,
|
|
||||||
size, end)) {
|
|
||||||
// got a spot
|
|
||||||
foundSpot = true;
|
|
||||||
area->SetBase(alignedBase);
|
|
||||||
break;
|
|
||||||
} else if (area->id != RESERVED_AREA_ID) {
|
|
||||||
// We didn't find a free spot - if there are any reserved areas,
|
|
||||||
// we can now test those for free space
|
|
||||||
// TODO: it would make sense to start with the biggest of them
|
|
||||||
it.Rewind();
|
|
||||||
next = it.Next();
|
|
||||||
for (last = NULL; next != NULL; next = it.Next()) {
|
|
||||||
if (next->id != RESERVED_AREA_ID) {
|
|
||||||
last = next;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: take free space after the reserved area into
|
|
||||||
// account!
|
|
||||||
addr_t alignedBase = ROUNDUP(next->Base(), alignment);
|
|
||||||
if (next->Base() == alignedBase && next->Size() == size) {
|
|
||||||
// The reserved area is entirely covered, and thus,
|
|
||||||
// removed
|
|
||||||
fAreas.Remove(next);
|
|
||||||
|
|
||||||
foundSpot = true;
|
|
||||||
area->SetBase(alignedBase);
|
|
||||||
free(next);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((next->protection & RESERVED_AVOID_BASE) == 0
|
|
||||||
&& alignedBase == next->Base()
|
|
||||||
&& next->Size() >= size) {
|
|
||||||
// The new area will be placed at the beginning of the
|
|
||||||
// reserved area and the reserved area will be offset
|
|
||||||
// and resized
|
|
||||||
foundSpot = true;
|
|
||||||
next->SetBase(next->Base() + size);
|
|
||||||
next->SetSize(next->Size() - size);
|
|
||||||
area->SetBase(alignedBase);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_valid_spot(next->Base(), alignedBase, size,
|
|
||||||
next->Base() + (next->Size() - 1))) {
|
|
||||||
// The new area will be placed at the end of the
|
|
||||||
// reserved area, and the reserved area will be resized
|
|
||||||
// to make space
|
|
||||||
alignedBase = ROUNDDOWN(
|
|
||||||
next->Base() + next->Size() - size, alignment);
|
|
||||||
|
|
||||||
foundSpot = true;
|
|
||||||
next->SetSize(alignedBase - next->Base());
|
|
||||||
area->SetBase(alignedBase);
|
|
||||||
last = next;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
last = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case B_BASE_ADDRESS:
|
|
||||||
{
|
|
||||||
// find a hole big enough for a new area beginning with "start"
|
|
||||||
if (last == NULL) {
|
|
||||||
// see if we can build it at the beginning of the specified
|
|
||||||
// start
|
|
||||||
if (next == NULL || next->Base() > start + (size - 1)) {
|
|
||||||
foundSpot = true;
|
|
||||||
area->SetBase(start);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
last = next;
|
|
||||||
next = it.Next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep walking
|
|
||||||
while (next != NULL) {
|
|
||||||
if (next->Base() - (last->Base() + last->Size()) >= size) {
|
|
||||||
// we found a spot (it'll be filled up below)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
last = next;
|
|
||||||
next = it.Next();
|
|
||||||
}
|
|
||||||
|
|
||||||
addr_t lastEnd = last->Base() + (last->Size() - 1);
|
|
||||||
if (next != NULL || end - lastEnd >= size) {
|
|
||||||
// got a spot
|
|
||||||
foundSpot = true;
|
|
||||||
if (lastEnd < start)
|
|
||||||
area->SetBase(start);
|
|
||||||
else
|
|
||||||
area->SetBase(lastEnd + 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we didn't find a free spot in the requested range, so we'll
|
|
||||||
// try again without any restrictions
|
|
||||||
start = fBase;
|
|
||||||
addressSpec = B_ANY_ADDRESS;
|
|
||||||
last = NULL;
|
|
||||||
goto second_chance;
|
|
||||||
}
|
|
||||||
|
|
||||||
case B_EXACT_ADDRESS:
|
|
||||||
// see if we can create it exactly here
|
|
||||||
if ((last == NULL || last->Base() + (last->Size() - 1) < start)
|
|
||||||
&& (next == NULL || next->Base() > start + (size - 1))) {
|
|
||||||
foundSpot = true;
|
|
||||||
area->SetBase(start);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!foundSpot)
|
|
||||||
return addressSpec == B_EXACT_ADDRESS ? B_BAD_VALUE : B_NO_MEMORY;
|
|
||||||
|
|
||||||
area->SetSize(size);
|
|
||||||
if (last)
|
|
||||||
fAreas.Insert(fAreas.GetNext(last), area);
|
|
||||||
else
|
|
||||||
fAreas.Insert(fAreas.Head(), area);
|
|
||||||
|
|
||||||
IncrementChangeCount();
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*static*/ int
|
/*static*/ int
|
||||||
VMAddressSpace::_DumpCommand(int argc, char** argv)
|
VMAddressSpace::_DumpCommand(int argc, char** argv)
|
||||||
{
|
{
|
||||||
@@ -889,10 +314,9 @@ VMAddressSpace::_DumpListCommand(int argc, char** argv)
|
|||||||
while (VMAddressSpace* space = it.Next()) {
|
while (VMAddressSpace* space = it.Next()) {
|
||||||
int32 areaCount = 0;
|
int32 areaCount = 0;
|
||||||
off_t areaSize = 0;
|
off_t areaSize = 0;
|
||||||
for (VMAddressSpaceAreaList::Iterator it = space->fAreas.GetIterator();
|
for (VMAddressSpace::AreaIterator areaIt = space->GetAreaIterator();
|
||||||
VMArea* area = it.Next();) {
|
VMArea* area = areaIt.Next();) {
|
||||||
if (area->id != RESERVED_AREA_ID
|
if (area->cache->type != CACHE_TYPE_NULL) {
|
||||||
&& area->cache->type != CACHE_TYPE_NULL) {
|
|
||||||
areaCount++;
|
areaCount++;
|
||||||
areaSize += area->Size();
|
areaSize += area->Size();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,645 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Copyright 2002-2009, Axel Dörfler, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
||||||
|
* Distributed under the terms of the NewOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "VMKernelAddressSpace.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <heap.h>
|
||||||
|
#include <thread.h>
|
||||||
|
#include <vm/vm.h>
|
||||||
|
#include <vm/VMArea.h>
|
||||||
|
|
||||||
|
|
||||||
|
//#define TRACE_VM
|
||||||
|
#ifdef TRACE_VM
|
||||||
|
# define TRACE(x) dprintf x
|
||||||
|
#else
|
||||||
|
# define TRACE(x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*! Verifies that an area with the given aligned base and size fits into
|
||||||
|
the spot defined by base and limit and checks for overflows.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
is_valid_spot(addr_t base, addr_t alignedBase, addr_t size, addr_t limit)
|
||||||
|
{
|
||||||
|
return (alignedBase >= base && alignedBase + (size - 1) > alignedBase
|
||||||
|
&& alignedBase + (size - 1) <= limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VMKernelAddressSpace::VMKernelAddressSpace(team_id id, addr_t base, size_t size)
|
||||||
|
:
|
||||||
|
VMAddressSpace(id, base, size, "kernel address space"),
|
||||||
|
fAreaHint(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VMKernelAddressSpace::~VMKernelAddressSpace()
|
||||||
|
{
|
||||||
|
panic("deleting the kernel aspace!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline VMArea*
|
||||||
|
VMKernelAddressSpace::FirstArea() const
|
||||||
|
{
|
||||||
|
VMArea* area = fAreas.Head();
|
||||||
|
while (area != NULL && area->id == RESERVED_AREA_ID)
|
||||||
|
area = fAreas.GetNext(area);
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline VMArea*
|
||||||
|
VMKernelAddressSpace::NextArea(VMArea* area) const
|
||||||
|
{
|
||||||
|
area = fAreas.GetNext(area);
|
||||||
|
while (area != NULL && area->id == RESERVED_AREA_ID)
|
||||||
|
area = fAreas.GetNext(area);
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! You must hold the address space's read lock.
|
||||||
|
VMArea*
|
||||||
|
VMKernelAddressSpace::LookupArea(addr_t address) const
|
||||||
|
{
|
||||||
|
// check the area hint first
|
||||||
|
if (fAreaHint != NULL && fAreaHint->ContainsAddress(address))
|
||||||
|
return fAreaHint;
|
||||||
|
|
||||||
|
for (VMAddressSpaceAreaList::ConstIterator it = fAreas.GetIterator();
|
||||||
|
VMArea* area = it.Next();) {
|
||||||
|
if (area->id == RESERVED_AREA_ID)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (area->ContainsAddress(address)) {
|
||||||
|
fAreaHint = area;
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! This inserts the area you pass into the address space.
|
||||||
|
It will also set the "_address" argument to its base address when
|
||||||
|
the call succeeds.
|
||||||
|
You need to hold the VMAddressSpace write lock.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
VMKernelAddressSpace::InsertArea(void** _address, uint32 addressSpec,
|
||||||
|
addr_t size, VMArea* area)
|
||||||
|
{
|
||||||
|
addr_t searchBase, searchEnd;
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
switch (addressSpec) {
|
||||||
|
case B_EXACT_ADDRESS:
|
||||||
|
searchBase = (addr_t)*_address;
|
||||||
|
searchEnd = (addr_t)*_address + (size - 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_BASE_ADDRESS:
|
||||||
|
searchBase = (addr_t)*_address;
|
||||||
|
searchEnd = fEndAddress;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_ANY_ADDRESS:
|
||||||
|
case B_ANY_KERNEL_ADDRESS:
|
||||||
|
case B_ANY_KERNEL_BLOCK_ADDRESS:
|
||||||
|
searchBase = fBase;
|
||||||
|
// TODO: remove this again when vm86 mode is moved into the kernel
|
||||||
|
// completely (currently needs a userland address space!)
|
||||||
|
if (searchBase == USER_BASE)
|
||||||
|
searchBase = USER_BASE_ANY;
|
||||||
|
searchEnd = fEndAddress;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = _InsertAreaSlot(searchBase, size, searchEnd, addressSpec, area);
|
||||||
|
if (status == B_OK) {
|
||||||
|
*_address = (void*)area->Base();
|
||||||
|
fFreeSpace -= area->Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! You must hold the address space's write lock.
|
||||||
|
void
|
||||||
|
VMKernelAddressSpace::RemoveArea(VMArea* area)
|
||||||
|
{
|
||||||
|
fAreas.Remove(area);
|
||||||
|
|
||||||
|
if (area->id != RESERVED_AREA_ID) {
|
||||||
|
IncrementChangeCount();
|
||||||
|
fFreeSpace += area->Size();
|
||||||
|
|
||||||
|
if (area == fAreaHint)
|
||||||
|
fAreaHint = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
VMKernelAddressSpace::CanResizeArea(VMArea* area, size_t newSize)
|
||||||
|
{
|
||||||
|
VMArea* next = fAreas.GetNext(area);
|
||||||
|
addr_t newEnd = area->Base() + (newSize - 1);
|
||||||
|
if (next == NULL) {
|
||||||
|
if (fEndAddress >= newEnd)
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (next->Base() > newEnd)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the area was created inside a reserved area, it can
|
||||||
|
// also be resized in that area
|
||||||
|
// TODO: if there is free space after the reserved area, it could
|
||||||
|
// be used as well...
|
||||||
|
if (next->id == RESERVED_AREA_ID && next->cache_offset <= area->Base()
|
||||||
|
&& next->Base() + (next->Size() - 1) >= newEnd) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
VMKernelAddressSpace::ResizeArea(VMArea* area, size_t newSize)
|
||||||
|
{
|
||||||
|
addr_t newEnd = area->Base() + (newSize - 1);
|
||||||
|
VMArea* next = fAreas.GetNext(area);
|
||||||
|
if (next != NULL && next->Base() <= newEnd) {
|
||||||
|
if (next->id != RESERVED_AREA_ID
|
||||||
|
|| next->cache_offset > area->Base()
|
||||||
|
|| next->Base() + (next->Size() - 1) < newEnd) {
|
||||||
|
panic("resize situation for area %p has changed although we "
|
||||||
|
"should have the address space lock", area);
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resize reserved area
|
||||||
|
addr_t offset = area->Base() + newSize - next->Base();
|
||||||
|
if (next->Size() <= offset) {
|
||||||
|
RemoveArea(next);
|
||||||
|
free(next);
|
||||||
|
} else {
|
||||||
|
status_t error = ResizeAreaHead(next, next->Size() - offset);
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResizeAreaTail(area, newSize);
|
||||||
|
// TODO: In case of error we should undo the change to the reserved
|
||||||
|
// area.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
VMKernelAddressSpace::ResizeAreaHead(VMArea* area, size_t size)
|
||||||
|
{
|
||||||
|
size_t oldSize = area->Size();
|
||||||
|
if (size == oldSize)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
area->SetBase(area->Base() + oldSize - size);
|
||||||
|
area->SetSize(size);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
VMKernelAddressSpace::ResizeAreaTail(VMArea* area, size_t size)
|
||||||
|
{
|
||||||
|
size_t oldSize = area->Size();
|
||||||
|
if (size == oldSize)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
area->SetSize(size);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
VMKernelAddressSpace::ReserveAddressRange(void** _address, uint32 addressSpec,
|
||||||
|
size_t size, uint32 flags)
|
||||||
|
{
|
||||||
|
// check to see if this address space has entered DELETE state
|
||||||
|
if (fDeleting) {
|
||||||
|
// okay, someone is trying to delete this address space now, so we
|
||||||
|
// can't insert the area, let's back out
|
||||||
|
return B_BAD_TEAM_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
VMArea* area = VMArea::CreateReserved(this, flags);
|
||||||
|
if (area == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
status_t status = InsertArea(_address, addressSpec, size, area);
|
||||||
|
if (status != B_OK) {
|
||||||
|
free(area);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
area->cache_offset = area->Base();
|
||||||
|
// we cache the original base address here
|
||||||
|
|
||||||
|
Get();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
VMKernelAddressSpace::UnreserveAddressRange(addr_t address, size_t size)
|
||||||
|
{
|
||||||
|
// check to see if this address space has entered DELETE state
|
||||||
|
if (fDeleting) {
|
||||||
|
// okay, someone is trying to delete this address space now, so we can't
|
||||||
|
// insert the area, so back out
|
||||||
|
return B_BAD_TEAM_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
// search area list and remove any matching reserved ranges
|
||||||
|
addr_t endAddress = address + (size - 1);
|
||||||
|
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||||
|
VMArea* area = it.Next();) {
|
||||||
|
// the area must be completely part of the reserved range
|
||||||
|
if (area->Base() + (area->Size() - 1) > endAddress)
|
||||||
|
break;
|
||||||
|
if (area->id == RESERVED_AREA_ID && area->Base() >= (addr_t)address) {
|
||||||
|
// remove reserved range
|
||||||
|
RemoveArea(area);
|
||||||
|
Put();
|
||||||
|
free(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
VMKernelAddressSpace::UnreserveAllAddressRanges()
|
||||||
|
{
|
||||||
|
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||||
|
VMArea* area = it.Next();) {
|
||||||
|
if (area->id == RESERVED_AREA_ID) {
|
||||||
|
RemoveArea(area);
|
||||||
|
Put();
|
||||||
|
free(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
VMKernelAddressSpace::Dump() const
|
||||||
|
{
|
||||||
|
VMAddressSpace::Dump();
|
||||||
|
kprintf("area_hint: %p\n", fAreaHint);
|
||||||
|
|
||||||
|
kprintf("area_list:\n");
|
||||||
|
|
||||||
|
for (VMAddressSpaceAreaList::ConstIterator it = fAreas.GetIterator();
|
||||||
|
VMArea* area = it.Next();) {
|
||||||
|
kprintf(" area 0x%lx: ", area->id);
|
||||||
|
kprintf("base_addr = 0x%lx ", area->Base());
|
||||||
|
kprintf("size = 0x%lx ", area->Size());
|
||||||
|
kprintf("name = '%s' ", area->name);
|
||||||
|
kprintf("protection = 0x%lx\n", area->protection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Finds a reserved area that covers the region spanned by \a start and
|
||||||
|
\a size, inserts the \a area into that region and makes sure that
|
||||||
|
there are reserved regions for the remaining parts.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
VMKernelAddressSpace::_InsertAreaIntoReservedRegion(addr_t start, size_t size,
|
||||||
|
VMArea* area)
|
||||||
|
{
|
||||||
|
VMArea* next;
|
||||||
|
|
||||||
|
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||||
|
(next = it.Next()) != NULL;) {
|
||||||
|
if (next->Base() <= start
|
||||||
|
&& next->Base() + (next->Size() - 1) >= start + (size - 1)) {
|
||||||
|
// This area covers the requested range
|
||||||
|
if (next->id != RESERVED_AREA_ID) {
|
||||||
|
// but it's not reserved space, it's a real area
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next == NULL)
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
// Now we have to transfer the requested part of the reserved
|
||||||
|
// range to the new area - and remove, resize or split the old
|
||||||
|
// reserved area.
|
||||||
|
|
||||||
|
if (start == next->Base()) {
|
||||||
|
// the area starts at the beginning of the reserved range
|
||||||
|
fAreas.Insert(next, area);
|
||||||
|
|
||||||
|
if (size == next->Size()) {
|
||||||
|
// the new area fully covers the reversed range
|
||||||
|
fAreas.Remove(next);
|
||||||
|
Put();
|
||||||
|
free(next);
|
||||||
|
} else {
|
||||||
|
// resize the reserved range behind the area
|
||||||
|
next->SetBase(next->Base() + size);
|
||||||
|
next->SetSize(next->Size() - size);
|
||||||
|
}
|
||||||
|
} else if (start + size == next->Base() + next->Size()) {
|
||||||
|
// the area is at the end of the reserved range
|
||||||
|
fAreas.Insert(fAreas.GetNext(next), area);
|
||||||
|
|
||||||
|
// resize the reserved range before the area
|
||||||
|
next->SetSize(start - next->Base());
|
||||||
|
} else {
|
||||||
|
// the area splits the reserved range into two separate ones
|
||||||
|
// we need a new reserved area to cover this space
|
||||||
|
VMArea* reserved = VMArea::CreateReserved(this, next->protection);
|
||||||
|
if (reserved == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
Get();
|
||||||
|
fAreas.Insert(fAreas.GetNext(next), reserved);
|
||||||
|
fAreas.Insert(reserved, area);
|
||||||
|
|
||||||
|
// resize regions
|
||||||
|
reserved->SetSize(next->Base() + next->Size() - start - size);
|
||||||
|
next->SetSize(start - next->Base());
|
||||||
|
reserved->SetBase(start + size);
|
||||||
|
reserved->cache_offset = next->cache_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
area->SetBase(start);
|
||||||
|
area->SetSize(size);
|
||||||
|
IncrementChangeCount();
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Must be called with this address space's write lock held */
|
||||||
|
status_t
|
||||||
|
VMKernelAddressSpace::_InsertAreaSlot(addr_t start, addr_t size, addr_t end,
|
||||||
|
uint32 addressSpec, VMArea* area)
|
||||||
|
{
|
||||||
|
VMArea* last = NULL;
|
||||||
|
VMArea* next;
|
||||||
|
bool foundSpot = false;
|
||||||
|
|
||||||
|
TRACE(("VMKernelAddressSpace::_InsertAreaSlot: address space %p, start "
|
||||||
|
"0x%lx, size %ld, end 0x%lx, addressSpec %ld, area %p\n", this, start,
|
||||||
|
size, end, addressSpec, area));
|
||||||
|
|
||||||
|
// do some sanity checking
|
||||||
|
if (start < fBase || size == 0 || end > fEndAddress
|
||||||
|
|| start + (size - 1) > end)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
if (addressSpec == B_EXACT_ADDRESS && area->id != RESERVED_AREA_ID) {
|
||||||
|
// search for a reserved area
|
||||||
|
status_t status = _InsertAreaIntoReservedRegion(start, size, area);
|
||||||
|
if (status == B_OK || status == B_BAD_VALUE)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// There was no reserved area, and the slot doesn't seem to be used
|
||||||
|
// already
|
||||||
|
// TODO: this could be further optimized.
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t alignment = B_PAGE_SIZE;
|
||||||
|
if (addressSpec == B_ANY_KERNEL_BLOCK_ADDRESS) {
|
||||||
|
// align the memory to the next power of two of the size
|
||||||
|
while (alignment < size)
|
||||||
|
alignment <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = ROUNDUP(start, alignment);
|
||||||
|
|
||||||
|
// walk up to the spot where we should start searching
|
||||||
|
second_chance:
|
||||||
|
VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||||
|
while ((next = it.Next()) != NULL) {
|
||||||
|
if (next->Base() > start + (size - 1)) {
|
||||||
|
// we have a winner
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the right spot depending on the address specification - the area
|
||||||
|
// will be inserted directly after "last" ("next" is not referenced anymore)
|
||||||
|
|
||||||
|
switch (addressSpec) {
|
||||||
|
case B_ANY_ADDRESS:
|
||||||
|
case B_ANY_KERNEL_ADDRESS:
|
||||||
|
case B_ANY_KERNEL_BLOCK_ADDRESS:
|
||||||
|
{
|
||||||
|
// find a hole big enough for a new area
|
||||||
|
if (last == NULL) {
|
||||||
|
// see if we can build it at the beginning of the virtual map
|
||||||
|
addr_t alignedBase = ROUNDUP(fBase, alignment);
|
||||||
|
if (is_valid_spot(fBase, alignedBase, size,
|
||||||
|
next == NULL ? end : next->Base())) {
|
||||||
|
foundSpot = true;
|
||||||
|
area->SetBase(alignedBase);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = next;
|
||||||
|
next = it.Next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep walking
|
||||||
|
while (next != NULL) {
|
||||||
|
addr_t alignedBase = ROUNDUP(last->Base() + last->Size(),
|
||||||
|
alignment);
|
||||||
|
if (is_valid_spot(last->Base() + (last->Size() - 1),
|
||||||
|
alignedBase, size, next->Base())) {
|
||||||
|
foundSpot = true;
|
||||||
|
area->SetBase(alignedBase);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = next;
|
||||||
|
next = it.Next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundSpot)
|
||||||
|
break;
|
||||||
|
|
||||||
|
addr_t alignedBase = ROUNDUP(last->Base() + last->Size(),
|
||||||
|
alignment);
|
||||||
|
if (is_valid_spot(last->Base() + (last->Size() - 1), alignedBase,
|
||||||
|
size, end)) {
|
||||||
|
// got a spot
|
||||||
|
foundSpot = true;
|
||||||
|
area->SetBase(alignedBase);
|
||||||
|
break;
|
||||||
|
} else if (area->id != RESERVED_AREA_ID) {
|
||||||
|
// We didn't find a free spot - if there are any reserved areas,
|
||||||
|
// we can now test those for free space
|
||||||
|
// TODO: it would make sense to start with the biggest of them
|
||||||
|
it.Rewind();
|
||||||
|
next = it.Next();
|
||||||
|
for (last = NULL; next != NULL; next = it.Next()) {
|
||||||
|
if (next->id != RESERVED_AREA_ID) {
|
||||||
|
last = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: take free space after the reserved area into
|
||||||
|
// account!
|
||||||
|
addr_t alignedBase = ROUNDUP(next->Base(), alignment);
|
||||||
|
if (next->Base() == alignedBase && next->Size() == size) {
|
||||||
|
// The reserved area is entirely covered, and thus,
|
||||||
|
// removed
|
||||||
|
fAreas.Remove(next);
|
||||||
|
|
||||||
|
foundSpot = true;
|
||||||
|
area->SetBase(alignedBase);
|
||||||
|
free(next);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((next->protection & RESERVED_AVOID_BASE) == 0
|
||||||
|
&& alignedBase == next->Base()
|
||||||
|
&& next->Size() >= size) {
|
||||||
|
// The new area will be placed at the beginning of the
|
||||||
|
// reserved area and the reserved area will be offset
|
||||||
|
// and resized
|
||||||
|
foundSpot = true;
|
||||||
|
next->SetBase(next->Base() + size);
|
||||||
|
next->SetSize(next->Size() - size);
|
||||||
|
area->SetBase(alignedBase);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_valid_spot(next->Base(), alignedBase, size,
|
||||||
|
next->Base() + (next->Size() - 1))) {
|
||||||
|
// The new area will be placed at the end of the
|
||||||
|
// reserved area, and the reserved area will be resized
|
||||||
|
// to make space
|
||||||
|
alignedBase = ROUNDDOWN(
|
||||||
|
next->Base() + next->Size() - size, alignment);
|
||||||
|
|
||||||
|
foundSpot = true;
|
||||||
|
next->SetSize(alignedBase - next->Base());
|
||||||
|
area->SetBase(alignedBase);
|
||||||
|
last = next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case B_BASE_ADDRESS:
|
||||||
|
{
|
||||||
|
// find a hole big enough for a new area beginning with "start"
|
||||||
|
if (last == NULL) {
|
||||||
|
// see if we can build it at the beginning of the specified
|
||||||
|
// start
|
||||||
|
if (next == NULL || next->Base() > start + (size - 1)) {
|
||||||
|
foundSpot = true;
|
||||||
|
area->SetBase(start);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = next;
|
||||||
|
next = it.Next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep walking
|
||||||
|
while (next != NULL) {
|
||||||
|
if (next->Base() - (last->Base() + last->Size()) >= size) {
|
||||||
|
// we found a spot (it'll be filled up below)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = next;
|
||||||
|
next = it.Next();
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_t lastEnd = last->Base() + (last->Size() - 1);
|
||||||
|
if (next != NULL || end - lastEnd >= size) {
|
||||||
|
// got a spot
|
||||||
|
foundSpot = true;
|
||||||
|
if (lastEnd < start)
|
||||||
|
area->SetBase(start);
|
||||||
|
else
|
||||||
|
area->SetBase(lastEnd + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we didn't find a free spot in the requested range, so we'll
|
||||||
|
// try again without any restrictions
|
||||||
|
start = fBase;
|
||||||
|
addressSpec = B_ANY_ADDRESS;
|
||||||
|
last = NULL;
|
||||||
|
goto second_chance;
|
||||||
|
}
|
||||||
|
|
||||||
|
case B_EXACT_ADDRESS:
|
||||||
|
// see if we can create it exactly here
|
||||||
|
if ((last == NULL || last->Base() + (last->Size() - 1) < start)
|
||||||
|
&& (next == NULL || next->Base() > start + (size - 1))) {
|
||||||
|
foundSpot = true;
|
||||||
|
area->SetBase(start);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundSpot)
|
||||||
|
return addressSpec == B_EXACT_ADDRESS ? B_BAD_VALUE : B_NO_MEMORY;
|
||||||
|
|
||||||
|
area->SetSize(size);
|
||||||
|
if (last)
|
||||||
|
fAreas.Insert(fAreas.GetNext(last), area);
|
||||||
|
else
|
||||||
|
fAreas.Insert(fAreas.Head(), area);
|
||||||
|
|
||||||
|
IncrementChangeCount();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Copyright 2002-2008, Axel Dörfler, [email protected]. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
||||||
|
* Distributed under the terms of the NewOS License.
|
||||||
|
*/
|
||||||
|
#ifndef VM_KERNEL_ADDRESS_SPACE_H
|
||||||
|
#define VM_KERNEL_ADDRESS_SPACE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <vm/VMAddressSpace.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct VMKernelAddressSpace : VMAddressSpace {
|
||||||
|
public:
|
||||||
|
VMKernelAddressSpace(team_id id, addr_t base,
|
||||||
|
size_t size);
|
||||||
|
virtual ~VMKernelAddressSpace();
|
||||||
|
|
||||||
|
virtual VMArea* FirstArea() const;
|
||||||
|
virtual VMArea* NextArea(VMArea* area) const;
|
||||||
|
|
||||||
|
virtual VMArea* LookupArea(addr_t address) const;
|
||||||
|
virtual status_t InsertArea(void** _address, uint32 addressSpec,
|
||||||
|
addr_t size, VMArea* area);
|
||||||
|
virtual void RemoveArea(VMArea* area);
|
||||||
|
|
||||||
|
virtual bool CanResizeArea(VMArea* area, size_t newSize);
|
||||||
|
virtual status_t ResizeArea(VMArea* area, size_t newSize);
|
||||||
|
virtual status_t ResizeAreaHead(VMArea* area, size_t size);
|
||||||
|
virtual status_t ResizeAreaTail(VMArea* area, size_t size);
|
||||||
|
|
||||||
|
virtual status_t ReserveAddressRange(void** _address,
|
||||||
|
uint32 addressSpec, size_t size,
|
||||||
|
uint32 flags);
|
||||||
|
virtual status_t UnreserveAddressRange(addr_t address,
|
||||||
|
size_t size);
|
||||||
|
virtual void UnreserveAllAddressRanges();
|
||||||
|
|
||||||
|
virtual void Dump() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
status_t _InsertAreaIntoReservedRegion(addr_t start,
|
||||||
|
size_t size, VMArea* area);
|
||||||
|
status_t _InsertAreaSlot(addr_t start, addr_t size,
|
||||||
|
addr_t end, uint32 addressSpec,
|
||||||
|
VMArea* area);
|
||||||
|
|
||||||
|
private:
|
||||||
|
VMAddressSpaceAreaList fAreas;
|
||||||
|
mutable VMArea* fAreaHint;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* VM_KERNEL_ADDRESS_SPACE_H */
|
||||||
@@ -0,0 +1,645 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Copyright 2002-2009, Axel Dörfler, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
||||||
|
* Distributed under the terms of the NewOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "VMUserAddressSpace.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <heap.h>
|
||||||
|
#include <thread.h>
|
||||||
|
#include <vm/vm.h>
|
||||||
|
#include <vm/VMArea.h>
|
||||||
|
|
||||||
|
|
||||||
|
//#define TRACE_VM
|
||||||
|
#ifdef TRACE_VM
|
||||||
|
# define TRACE(x) dprintf x
|
||||||
|
#else
|
||||||
|
# define TRACE(x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*! Verifies that an area with the given aligned base and size fits into
|
||||||
|
the spot defined by base and limit and checks for overflows.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
is_valid_spot(addr_t base, addr_t alignedBase, addr_t size, addr_t limit)
|
||||||
|
{
|
||||||
|
return (alignedBase >= base && alignedBase + (size - 1) > alignedBase
|
||||||
|
&& alignedBase + (size - 1) <= limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VMUserAddressSpace::VMUserAddressSpace(team_id id, addr_t base, size_t size)
|
||||||
|
:
|
||||||
|
VMAddressSpace(id, base, size, "kernel address space"),
|
||||||
|
fAreaHint(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VMUserAddressSpace::~VMUserAddressSpace()
|
||||||
|
{
|
||||||
|
panic("deleting the kernel aspace!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline VMArea*
|
||||||
|
VMUserAddressSpace::FirstArea() const
|
||||||
|
{
|
||||||
|
VMArea* area = fAreas.Head();
|
||||||
|
while (area != NULL && area->id == RESERVED_AREA_ID)
|
||||||
|
area = fAreas.GetNext(area);
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline VMArea*
|
||||||
|
VMUserAddressSpace::NextArea(VMArea* area) const
|
||||||
|
{
|
||||||
|
area = fAreas.GetNext(area);
|
||||||
|
while (area != NULL && area->id == RESERVED_AREA_ID)
|
||||||
|
area = fAreas.GetNext(area);
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! You must hold the address space's read lock.
|
||||||
|
VMArea*
|
||||||
|
VMUserAddressSpace::LookupArea(addr_t address) const
|
||||||
|
{
|
||||||
|
// check the area hint first
|
||||||
|
if (fAreaHint != NULL && fAreaHint->ContainsAddress(address))
|
||||||
|
return fAreaHint;
|
||||||
|
|
||||||
|
for (VMAddressSpaceAreaList::ConstIterator it = fAreas.GetIterator();
|
||||||
|
VMArea* area = it.Next();) {
|
||||||
|
if (area->id == RESERVED_AREA_ID)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (area->ContainsAddress(address)) {
|
||||||
|
fAreaHint = area;
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! This inserts the area you pass into the address space.
|
||||||
|
It will also set the "_address" argument to its base address when
|
||||||
|
the call succeeds.
|
||||||
|
You need to hold the VMAddressSpace write lock.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
VMUserAddressSpace::InsertArea(void** _address, uint32 addressSpec,
|
||||||
|
addr_t size, VMArea* area)
|
||||||
|
{
|
||||||
|
addr_t searchBase, searchEnd;
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
switch (addressSpec) {
|
||||||
|
case B_EXACT_ADDRESS:
|
||||||
|
searchBase = (addr_t)*_address;
|
||||||
|
searchEnd = (addr_t)*_address + (size - 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_BASE_ADDRESS:
|
||||||
|
searchBase = (addr_t)*_address;
|
||||||
|
searchEnd = fEndAddress;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_ANY_ADDRESS:
|
||||||
|
case B_ANY_KERNEL_ADDRESS:
|
||||||
|
case B_ANY_KERNEL_BLOCK_ADDRESS:
|
||||||
|
searchBase = fBase;
|
||||||
|
// TODO: remove this again when vm86 mode is moved into the kernel
|
||||||
|
// completely (currently needs a userland address space!)
|
||||||
|
if (searchBase == USER_BASE)
|
||||||
|
searchBase = USER_BASE_ANY;
|
||||||
|
searchEnd = fEndAddress;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = _InsertAreaSlot(searchBase, size, searchEnd, addressSpec, area);
|
||||||
|
if (status == B_OK) {
|
||||||
|
*_address = (void*)area->Base();
|
||||||
|
fFreeSpace -= area->Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! You must hold the address space's write lock.
|
||||||
|
void
|
||||||
|
VMUserAddressSpace::RemoveArea(VMArea* area)
|
||||||
|
{
|
||||||
|
fAreas.Remove(area);
|
||||||
|
|
||||||
|
if (area->id != RESERVED_AREA_ID) {
|
||||||
|
IncrementChangeCount();
|
||||||
|
fFreeSpace += area->Size();
|
||||||
|
|
||||||
|
if (area == fAreaHint)
|
||||||
|
fAreaHint = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
VMUserAddressSpace::CanResizeArea(VMArea* area, size_t newSize)
|
||||||
|
{
|
||||||
|
VMArea* next = fAreas.GetNext(area);
|
||||||
|
addr_t newEnd = area->Base() + (newSize - 1);
|
||||||
|
if (next == NULL) {
|
||||||
|
if (fEndAddress >= newEnd)
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (next->Base() > newEnd)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the area was created inside a reserved area, it can
|
||||||
|
// also be resized in that area
|
||||||
|
// TODO: if there is free space after the reserved area, it could
|
||||||
|
// be used as well...
|
||||||
|
if (next->id == RESERVED_AREA_ID && next->cache_offset <= area->Base()
|
||||||
|
&& next->Base() + (next->Size() - 1) >= newEnd) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
VMUserAddressSpace::ResizeArea(VMArea* area, size_t newSize)
|
||||||
|
{
|
||||||
|
addr_t newEnd = area->Base() + (newSize - 1);
|
||||||
|
VMArea* next = fAreas.GetNext(area);
|
||||||
|
if (next != NULL && next->Base() <= newEnd) {
|
||||||
|
if (next->id != RESERVED_AREA_ID
|
||||||
|
|| next->cache_offset > area->Base()
|
||||||
|
|| next->Base() + (next->Size() - 1) < newEnd) {
|
||||||
|
panic("resize situation for area %p has changed although we "
|
||||||
|
"should have the address space lock", area);
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resize reserved area
|
||||||
|
addr_t offset = area->Base() + newSize - next->Base();
|
||||||
|
if (next->Size() <= offset) {
|
||||||
|
RemoveArea(next);
|
||||||
|
free(next);
|
||||||
|
} else {
|
||||||
|
status_t error = ResizeAreaHead(next, next->Size() - offset);
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResizeAreaTail(area, newSize);
|
||||||
|
// TODO: In case of error we should undo the change to the reserved
|
||||||
|
// area.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
VMUserAddressSpace::ResizeAreaHead(VMArea* area, size_t size)
|
||||||
|
{
|
||||||
|
size_t oldSize = area->Size();
|
||||||
|
if (size == oldSize)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
area->SetBase(area->Base() + oldSize - size);
|
||||||
|
area->SetSize(size);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
VMUserAddressSpace::ResizeAreaTail(VMArea* area, size_t size)
|
||||||
|
{
|
||||||
|
size_t oldSize = area->Size();
|
||||||
|
if (size == oldSize)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
area->SetSize(size);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
VMUserAddressSpace::ReserveAddressRange(void** _address, uint32 addressSpec,
|
||||||
|
size_t size, uint32 flags)
|
||||||
|
{
|
||||||
|
// check to see if this address space has entered DELETE state
|
||||||
|
if (fDeleting) {
|
||||||
|
// okay, someone is trying to delete this address space now, so we
|
||||||
|
// can't insert the area, let's back out
|
||||||
|
return B_BAD_TEAM_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
VMArea* area = VMArea::CreateReserved(this, flags);
|
||||||
|
if (area == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
status_t status = InsertArea(_address, addressSpec, size, area);
|
||||||
|
if (status != B_OK) {
|
||||||
|
free(area);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
area->cache_offset = area->Base();
|
||||||
|
// we cache the original base address here
|
||||||
|
|
||||||
|
Get();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
VMUserAddressSpace::UnreserveAddressRange(addr_t address, size_t size)
|
||||||
|
{
|
||||||
|
// check to see if this address space has entered DELETE state
|
||||||
|
if (fDeleting) {
|
||||||
|
// okay, someone is trying to delete this address space now, so we can't
|
||||||
|
// insert the area, so back out
|
||||||
|
return B_BAD_TEAM_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
// search area list and remove any matching reserved ranges
|
||||||
|
addr_t endAddress = address + (size - 1);
|
||||||
|
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||||
|
VMArea* area = it.Next();) {
|
||||||
|
// the area must be completely part of the reserved range
|
||||||
|
if (area->Base() + (area->Size() - 1) > endAddress)
|
||||||
|
break;
|
||||||
|
if (area->id == RESERVED_AREA_ID && area->Base() >= (addr_t)address) {
|
||||||
|
// remove reserved range
|
||||||
|
RemoveArea(area);
|
||||||
|
Put();
|
||||||
|
free(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
VMUserAddressSpace::UnreserveAllAddressRanges()
|
||||||
|
{
|
||||||
|
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||||
|
VMArea* area = it.Next();) {
|
||||||
|
if (area->id == RESERVED_AREA_ID) {
|
||||||
|
RemoveArea(area);
|
||||||
|
Put();
|
||||||
|
free(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
VMUserAddressSpace::Dump() const
|
||||||
|
{
|
||||||
|
VMAddressSpace::Dump();
|
||||||
|
kprintf("area_hint: %p\n", fAreaHint);
|
||||||
|
|
||||||
|
kprintf("area_list:\n");
|
||||||
|
|
||||||
|
for (VMAddressSpaceAreaList::ConstIterator it = fAreas.GetIterator();
|
||||||
|
VMArea* area = it.Next();) {
|
||||||
|
kprintf(" area 0x%lx: ", area->id);
|
||||||
|
kprintf("base_addr = 0x%lx ", area->Base());
|
||||||
|
kprintf("size = 0x%lx ", area->Size());
|
||||||
|
kprintf("name = '%s' ", area->name);
|
||||||
|
kprintf("protection = 0x%lx\n", area->protection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Finds a reserved area that covers the region spanned by \a start and
|
||||||
|
\a size, inserts the \a area into that region and makes sure that
|
||||||
|
there are reserved regions for the remaining parts.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
VMUserAddressSpace::_InsertAreaIntoReservedRegion(addr_t start, size_t size,
|
||||||
|
VMArea* area)
|
||||||
|
{
|
||||||
|
VMArea* next;
|
||||||
|
|
||||||
|
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||||
|
(next = it.Next()) != NULL;) {
|
||||||
|
if (next->Base() <= start
|
||||||
|
&& next->Base() + (next->Size() - 1) >= start + (size - 1)) {
|
||||||
|
// This area covers the requested range
|
||||||
|
if (next->id != RESERVED_AREA_ID) {
|
||||||
|
// but it's not reserved space, it's a real area
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next == NULL)
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
// Now we have to transfer the requested part of the reserved
|
||||||
|
// range to the new area - and remove, resize or split the old
|
||||||
|
// reserved area.
|
||||||
|
|
||||||
|
if (start == next->Base()) {
|
||||||
|
// the area starts at the beginning of the reserved range
|
||||||
|
fAreas.Insert(next, area);
|
||||||
|
|
||||||
|
if (size == next->Size()) {
|
||||||
|
// the new area fully covers the reversed range
|
||||||
|
fAreas.Remove(next);
|
||||||
|
Put();
|
||||||
|
free(next);
|
||||||
|
} else {
|
||||||
|
// resize the reserved range behind the area
|
||||||
|
next->SetBase(next->Base() + size);
|
||||||
|
next->SetSize(next->Size() - size);
|
||||||
|
}
|
||||||
|
} else if (start + size == next->Base() + next->Size()) {
|
||||||
|
// the area is at the end of the reserved range
|
||||||
|
fAreas.Insert(fAreas.GetNext(next), area);
|
||||||
|
|
||||||
|
// resize the reserved range before the area
|
||||||
|
next->SetSize(start - next->Base());
|
||||||
|
} else {
|
||||||
|
// the area splits the reserved range into two separate ones
|
||||||
|
// we need a new reserved area to cover this space
|
||||||
|
VMArea* reserved = VMArea::CreateReserved(this, next->protection);
|
||||||
|
if (reserved == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
Get();
|
||||||
|
fAreas.Insert(fAreas.GetNext(next), reserved);
|
||||||
|
fAreas.Insert(reserved, area);
|
||||||
|
|
||||||
|
// resize regions
|
||||||
|
reserved->SetSize(next->Base() + next->Size() - start - size);
|
||||||
|
next->SetSize(start - next->Base());
|
||||||
|
reserved->SetBase(start + size);
|
||||||
|
reserved->cache_offset = next->cache_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
area->SetBase(start);
|
||||||
|
area->SetSize(size);
|
||||||
|
IncrementChangeCount();
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Must be called with this address space's write lock held */
|
||||||
|
status_t
|
||||||
|
VMUserAddressSpace::_InsertAreaSlot(addr_t start, addr_t size, addr_t end,
|
||||||
|
uint32 addressSpec, VMArea* area)
|
||||||
|
{
|
||||||
|
VMArea* last = NULL;
|
||||||
|
VMArea* next;
|
||||||
|
bool foundSpot = false;
|
||||||
|
|
||||||
|
TRACE(("VMUserAddressSpace::_InsertAreaSlot: address space %p, start "
|
||||||
|
"0x%lx, size %ld, end 0x%lx, addressSpec %ld, area %p\n", this, start,
|
||||||
|
size, end, addressSpec, area));
|
||||||
|
|
||||||
|
// do some sanity checking
|
||||||
|
if (start < fBase || size == 0 || end > fEndAddress
|
||||||
|
|| start + (size - 1) > end)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
if (addressSpec == B_EXACT_ADDRESS && area->id != RESERVED_AREA_ID) {
|
||||||
|
// search for a reserved area
|
||||||
|
status_t status = _InsertAreaIntoReservedRegion(start, size, area);
|
||||||
|
if (status == B_OK || status == B_BAD_VALUE)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// There was no reserved area, and the slot doesn't seem to be used
|
||||||
|
// already
|
||||||
|
// TODO: this could be further optimized.
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t alignment = B_PAGE_SIZE;
|
||||||
|
if (addressSpec == B_ANY_KERNEL_BLOCK_ADDRESS) {
|
||||||
|
// align the memory to the next power of two of the size
|
||||||
|
while (alignment < size)
|
||||||
|
alignment <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = ROUNDUP(start, alignment);
|
||||||
|
|
||||||
|
// walk up to the spot where we should start searching
|
||||||
|
second_chance:
|
||||||
|
VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||||
|
while ((next = it.Next()) != NULL) {
|
||||||
|
if (next->Base() > start + (size - 1)) {
|
||||||
|
// we have a winner
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the right spot depending on the address specification - the area
|
||||||
|
// will be inserted directly after "last" ("next" is not referenced anymore)
|
||||||
|
|
||||||
|
switch (addressSpec) {
|
||||||
|
case B_ANY_ADDRESS:
|
||||||
|
case B_ANY_KERNEL_ADDRESS:
|
||||||
|
case B_ANY_KERNEL_BLOCK_ADDRESS:
|
||||||
|
{
|
||||||
|
// find a hole big enough for a new area
|
||||||
|
if (last == NULL) {
|
||||||
|
// see if we can build it at the beginning of the virtual map
|
||||||
|
addr_t alignedBase = ROUNDUP(fBase, alignment);
|
||||||
|
if (is_valid_spot(fBase, alignedBase, size,
|
||||||
|
next == NULL ? end : next->Base())) {
|
||||||
|
foundSpot = true;
|
||||||
|
area->SetBase(alignedBase);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = next;
|
||||||
|
next = it.Next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep walking
|
||||||
|
while (next != NULL) {
|
||||||
|
addr_t alignedBase = ROUNDUP(last->Base() + last->Size(),
|
||||||
|
alignment);
|
||||||
|
if (is_valid_spot(last->Base() + (last->Size() - 1),
|
||||||
|
alignedBase, size, next->Base())) {
|
||||||
|
foundSpot = true;
|
||||||
|
area->SetBase(alignedBase);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = next;
|
||||||
|
next = it.Next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundSpot)
|
||||||
|
break;
|
||||||
|
|
||||||
|
addr_t alignedBase = ROUNDUP(last->Base() + last->Size(),
|
||||||
|
alignment);
|
||||||
|
if (is_valid_spot(last->Base() + (last->Size() - 1), alignedBase,
|
||||||
|
size, end)) {
|
||||||
|
// got a spot
|
||||||
|
foundSpot = true;
|
||||||
|
area->SetBase(alignedBase);
|
||||||
|
break;
|
||||||
|
} else if (area->id != RESERVED_AREA_ID) {
|
||||||
|
// We didn't find a free spot - if there are any reserved areas,
|
||||||
|
// we can now test those for free space
|
||||||
|
// TODO: it would make sense to start with the biggest of them
|
||||||
|
it.Rewind();
|
||||||
|
next = it.Next();
|
||||||
|
for (last = NULL; next != NULL; next = it.Next()) {
|
||||||
|
if (next->id != RESERVED_AREA_ID) {
|
||||||
|
last = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: take free space after the reserved area into
|
||||||
|
// account!
|
||||||
|
addr_t alignedBase = ROUNDUP(next->Base(), alignment);
|
||||||
|
if (next->Base() == alignedBase && next->Size() == size) {
|
||||||
|
// The reserved area is entirely covered, and thus,
|
||||||
|
// removed
|
||||||
|
fAreas.Remove(next);
|
||||||
|
|
||||||
|
foundSpot = true;
|
||||||
|
area->SetBase(alignedBase);
|
||||||
|
free(next);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((next->protection & RESERVED_AVOID_BASE) == 0
|
||||||
|
&& alignedBase == next->Base()
|
||||||
|
&& next->Size() >= size) {
|
||||||
|
// The new area will be placed at the beginning of the
|
||||||
|
// reserved area and the reserved area will be offset
|
||||||
|
// and resized
|
||||||
|
foundSpot = true;
|
||||||
|
next->SetBase(next->Base() + size);
|
||||||
|
next->SetSize(next->Size() - size);
|
||||||
|
area->SetBase(alignedBase);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_valid_spot(next->Base(), alignedBase, size,
|
||||||
|
next->Base() + (next->Size() - 1))) {
|
||||||
|
// The new area will be placed at the end of the
|
||||||
|
// reserved area, and the reserved area will be resized
|
||||||
|
// to make space
|
||||||
|
alignedBase = ROUNDDOWN(
|
||||||
|
next->Base() + next->Size() - size, alignment);
|
||||||
|
|
||||||
|
foundSpot = true;
|
||||||
|
next->SetSize(alignedBase - next->Base());
|
||||||
|
area->SetBase(alignedBase);
|
||||||
|
last = next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case B_BASE_ADDRESS:
|
||||||
|
{
|
||||||
|
// find a hole big enough for a new area beginning with "start"
|
||||||
|
if (last == NULL) {
|
||||||
|
// see if we can build it at the beginning of the specified
|
||||||
|
// start
|
||||||
|
if (next == NULL || next->Base() > start + (size - 1)) {
|
||||||
|
foundSpot = true;
|
||||||
|
area->SetBase(start);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = next;
|
||||||
|
next = it.Next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep walking
|
||||||
|
while (next != NULL) {
|
||||||
|
if (next->Base() - (last->Base() + last->Size()) >= size) {
|
||||||
|
// we found a spot (it'll be filled up below)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = next;
|
||||||
|
next = it.Next();
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_t lastEnd = last->Base() + (last->Size() - 1);
|
||||||
|
if (next != NULL || end - lastEnd >= size) {
|
||||||
|
// got a spot
|
||||||
|
foundSpot = true;
|
||||||
|
if (lastEnd < start)
|
||||||
|
area->SetBase(start);
|
||||||
|
else
|
||||||
|
area->SetBase(lastEnd + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we didn't find a free spot in the requested range, so we'll
|
||||||
|
// try again without any restrictions
|
||||||
|
start = fBase;
|
||||||
|
addressSpec = B_ANY_ADDRESS;
|
||||||
|
last = NULL;
|
||||||
|
goto second_chance;
|
||||||
|
}
|
||||||
|
|
||||||
|
case B_EXACT_ADDRESS:
|
||||||
|
// see if we can create it exactly here
|
||||||
|
if ((last == NULL || last->Base() + (last->Size() - 1) < start)
|
||||||
|
&& (next == NULL || next->Base() > start + (size - 1))) {
|
||||||
|
foundSpot = true;
|
||||||
|
area->SetBase(start);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundSpot)
|
||||||
|
return addressSpec == B_EXACT_ADDRESS ? B_BAD_VALUE : B_NO_MEMORY;
|
||||||
|
|
||||||
|
area->SetSize(size);
|
||||||
|
if (last)
|
||||||
|
fAreas.Insert(fAreas.GetNext(last), area);
|
||||||
|
else
|
||||||
|
fAreas.Insert(fAreas.Head(), area);
|
||||||
|
|
||||||
|
IncrementChangeCount();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Copyright 2002-2008, Axel Dörfler, [email protected]. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
||||||
|
* Distributed under the terms of the NewOS License.
|
||||||
|
*/
|
||||||
|
#ifndef VM_USER_ADDRESS_SPACE_H
|
||||||
|
#define VM_USER_ADDRESS_SPACE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <vm/VMAddressSpace.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct VMUserAddressSpace : VMAddressSpace {
|
||||||
|
public:
|
||||||
|
VMUserAddressSpace(team_id id, addr_t base,
|
||||||
|
size_t size);
|
||||||
|
virtual ~VMUserAddressSpace();
|
||||||
|
|
||||||
|
virtual VMArea* FirstArea() const;
|
||||||
|
virtual VMArea* NextArea(VMArea* area) const;
|
||||||
|
|
||||||
|
virtual VMArea* LookupArea(addr_t address) const;
|
||||||
|
virtual status_t InsertArea(void** _address, uint32 addressSpec,
|
||||||
|
addr_t size, VMArea* area);
|
||||||
|
virtual void RemoveArea(VMArea* area);
|
||||||
|
|
||||||
|
virtual bool CanResizeArea(VMArea* area, size_t newSize);
|
||||||
|
virtual status_t ResizeArea(VMArea* area, size_t newSize);
|
||||||
|
virtual status_t ResizeAreaHead(VMArea* area, size_t size);
|
||||||
|
virtual status_t ResizeAreaTail(VMArea* area, size_t size);
|
||||||
|
|
||||||
|
virtual status_t ReserveAddressRange(void** _address,
|
||||||
|
uint32 addressSpec, size_t size,
|
||||||
|
uint32 flags);
|
||||||
|
virtual status_t UnreserveAddressRange(addr_t address,
|
||||||
|
size_t size);
|
||||||
|
virtual void UnreserveAllAddressRanges();
|
||||||
|
|
||||||
|
virtual void Dump() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
status_t _InsertAreaIntoReservedRegion(addr_t start,
|
||||||
|
size_t size, VMArea* area);
|
||||||
|
status_t _InsertAreaSlot(addr_t start, addr_t size,
|
||||||
|
addr_t end, uint32 addressSpec,
|
||||||
|
VMArea* area);
|
||||||
|
|
||||||
|
private:
|
||||||
|
VMAddressSpaceAreaList fAreas;
|
||||||
|
mutable VMArea* fAreaHint;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* VM_USER_ADDRESS_SPACE_H */
|
||||||
Reference in New Issue
Block a user