* Introduced phys_addr_range type, an equivalent to addr_range for physical
address ranges, and a set of support functions working with it. * Changed the type of the kernel_args physical address range arrays to phys_addr_range and adjusted the code working with those. * Removed a bunch of duplicated address range code in the PPC's mmu.cpp. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36947 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright 2010, Ingo Weinhold, [email protected].
|
||||||
* Copyright 2004-2007, Axel Dörfler, [email protected]. All rights reserved.
|
* Copyright 2004-2007, Axel Dörfler, [email protected]. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
@@ -11,26 +12,41 @@
|
|||||||
|
|
||||||
typedef struct addr_range {
|
typedef struct addr_range {
|
||||||
addr_t start;
|
addr_t start;
|
||||||
addr_t size;
|
size_t size;
|
||||||
} addr_range;
|
} addr_range;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct phys_addr_range {
|
||||||
|
phys_addr_t start;
|
||||||
|
phys_size_t size;
|
||||||
|
} phys_addr_range;
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
status_t insert_address_range(addr_range* ranges, uint32* _numRanges,
|
status_t insert_address_range(addr_range* ranges, uint32* _numRanges,
|
||||||
uint32 maxRanges, addr_t start, uint32 size);
|
uint32 maxRanges, addr_t start, size_t size);
|
||||||
status_t remove_address_range(addr_range* ranges, uint32* _numRanges,
|
status_t remove_address_range(addr_range* ranges, uint32* _numRanges,
|
||||||
uint32 maxRanges, addr_t start, uint32 size);
|
uint32 maxRanges, addr_t start, size_t size);
|
||||||
bool get_free_address_range(addr_range* ranges, uint32 numRanges, addr_t base,
|
bool get_free_address_range(addr_range* ranges, uint32 numRanges, addr_t base,
|
||||||
size_t size, addr_t* _rangeBase);
|
size_t size, addr_t* _rangeBase);
|
||||||
bool is_address_range_covered(addr_range* ranges, uint32 numRanges, addr_t base,
|
bool is_address_range_covered(addr_range* ranges, uint32 numRanges, addr_t base,
|
||||||
size_t size);
|
size_t size);
|
||||||
|
|
||||||
status_t insert_physical_memory_range(addr_t start, uint32 size);
|
status_t insert_physical_address_range(phys_addr_range* ranges,
|
||||||
status_t insert_physical_allocated_range(addr_t start, uint32 size);
|
uint32* _numRanges, uint32 maxRanges, phys_addr_t start, phys_size_t size);
|
||||||
status_t insert_virtual_allocated_range(addr_t start, uint32 size);
|
status_t remove_physical_address_range(phys_addr_range* ranges,
|
||||||
|
uint32* _numRanges, uint32 maxRanges, phys_addr_t start, phys_size_t size);
|
||||||
|
bool get_free_physical_address_range(phys_addr_range* ranges, uint32 numRanges,
|
||||||
|
phys_addr_t base, phys_size_t size, phys_addr_t* _rangeBase);
|
||||||
|
bool is_physical_address_range_covered(phys_addr_range* ranges,
|
||||||
|
uint32 numRanges, phys_addr_t base, phys_size_t size);
|
||||||
|
|
||||||
|
status_t insert_physical_memory_range(phys_addr_t start, phys_size_t size);
|
||||||
|
status_t insert_physical_allocated_range(phys_addr_t start, phys_size_t size);
|
||||||
|
status_t insert_virtual_allocated_range(addr_t start, size_t size);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,9 +47,9 @@ typedef struct kernel_args {
|
|||||||
struct preloaded_image *preloaded_images;
|
struct preloaded_image *preloaded_images;
|
||||||
|
|
||||||
uint32 num_physical_memory_ranges;
|
uint32 num_physical_memory_ranges;
|
||||||
addr_range physical_memory_range[MAX_PHYSICAL_MEMORY_RANGE];
|
phys_addr_range physical_memory_range[MAX_PHYSICAL_MEMORY_RANGE];
|
||||||
uint32 num_physical_allocated_ranges;
|
uint32 num_physical_allocated_ranges;
|
||||||
addr_range physical_allocated_range[MAX_PHYSICAL_ALLOCATED_RANGE];
|
phys_addr_range physical_allocated_range[MAX_PHYSICAL_ALLOCATED_RANGE];
|
||||||
uint32 num_virtual_allocated_ranges;
|
uint32 num_virtual_allocated_ranges;
|
||||||
addr_range virtual_allocated_range[MAX_VIRTUAL_ALLOCATED_RANGE];
|
addr_range virtual_allocated_range[MAX_VIRTUAL_ALLOCATED_RANGE];
|
||||||
uint32 num_kernel_args_ranges;
|
uint32 num_kernel_args_ranges;
|
||||||
@@ -63,7 +63,7 @@ typedef struct kernel_args {
|
|||||||
struct driver_settings_file *driver_settings;
|
struct driver_settings_file *driver_settings;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
addr_range physical_buffer;
|
phys_addr_range physical_buffer;
|
||||||
uint32 bytes_per_row;
|
uint32 bytes_per_row;
|
||||||
uint16 width;
|
uint16 width;
|
||||||
uint16 height;
|
uint16 height;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright 2010, Ingo Weinhold, [email protected].
|
||||||
* Copyright 2004-2008, Axel Dörfler, [email protected].
|
* Copyright 2004-2008, Axel Dörfler, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
@@ -24,8 +25,9 @@ static void* sLast;
|
|||||||
static size_t sFree = kChunkSize;
|
static size_t sFree = kChunkSize;
|
||||||
|
|
||||||
|
|
||||||
|
template<typename RangeType>
|
||||||
static void
|
static void
|
||||||
remove_range_index(addr_range* ranges, uint32& numRanges, uint32 index)
|
remove_range_index(RangeType* ranges, uint32& numRanges, uint32 index)
|
||||||
{
|
{
|
||||||
if (index + 1 == numRanges) {
|
if (index + 1 == numRanges) {
|
||||||
// remove last range
|
// remove last range
|
||||||
@@ -34,43 +36,25 @@ remove_range_index(addr_range* ranges, uint32& numRanges, uint32 index)
|
|||||||
}
|
}
|
||||||
|
|
||||||
memmove(&ranges[index], &ranges[index + 1],
|
memmove(&ranges[index], &ranges[index + 1],
|
||||||
sizeof(addr_range) * (numRanges - 1 - index));
|
sizeof(RangeType) * (numRanges - 1 - index));
|
||||||
numRanges--;
|
numRanges--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename RangeType, typename AddressType, typename SizeType>
|
||||||
static status_t
|
static status_t
|
||||||
add_kernel_args_range(void* start, uint32 size)
|
insert_range(RangeType* ranges, uint32* _numRanges, uint32 maxRanges,
|
||||||
{
|
AddressType start, SizeType size)
|
||||||
return insert_address_range(gKernelArgs.kernel_args_range,
|
|
||||||
&gKernelArgs.num_kernel_args_ranges, MAX_KERNEL_ARGS_RANGE,
|
|
||||||
(addr_t)start, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - addr_range utility
|
|
||||||
|
|
||||||
|
|
||||||
/*! Inserts the specified (start, size) pair (aka range) in the
|
|
||||||
addr_range array.
|
|
||||||
It will extend existing ranges in order to have as little
|
|
||||||
ranges in the array as possible.
|
|
||||||
Returns B_OK on success, or B_ENTRY_NOT_FOUND if there was
|
|
||||||
no free array entry available anymore.
|
|
||||||
*/
|
|
||||||
extern "C" status_t
|
|
||||||
insert_address_range(addr_range* ranges, uint32* _numRanges, uint32 maxRanges,
|
|
||||||
addr_t start, uint32 size)
|
|
||||||
{
|
{
|
||||||
uint32 numRanges = *_numRanges;
|
uint32 numRanges = *_numRanges;
|
||||||
|
|
||||||
start = ROUNDDOWN(start, B_PAGE_SIZE);
|
start = ROUNDDOWN(start, B_PAGE_SIZE);
|
||||||
size = ROUNDUP(size, B_PAGE_SIZE);
|
size = ROUNDUP(size, B_PAGE_SIZE);
|
||||||
addr_t end = start + size;
|
AddressType end = start + size;
|
||||||
|
|
||||||
for (uint32 i = 0; i < numRanges; i++) {
|
for (uint32 i = 0; i < numRanges; i++) {
|
||||||
addr_t rangeStart = ranges[i].start;
|
AddressType rangeStart = ranges[i].start;
|
||||||
addr_t rangeEnd = rangeStart + ranges[i].size;
|
AddressType rangeEnd = rangeStart + ranges[i].size;
|
||||||
|
|
||||||
if (end < rangeStart || start > rangeEnd) {
|
if (end < rangeStart || start > rangeEnd) {
|
||||||
// ranges don't intersect or touch each other
|
// ranges don't intersect or touch each other
|
||||||
@@ -99,8 +83,8 @@ insert_address_range(addr_range* ranges, uint32* _numRanges, uint32 maxRanges,
|
|||||||
|
|
||||||
rangeStart = ranges[i].start;
|
rangeStart = ranges[i].start;
|
||||||
rangeEnd = rangeStart + ranges[i].size;
|
rangeEnd = rangeStart + ranges[i].size;
|
||||||
addr_t joinStart = ranges[j].start;
|
AddressType joinStart = ranges[j].start;
|
||||||
addr_t joinEnd = joinStart + ranges[j].size;
|
AddressType joinEnd = joinStart + ranges[j].size;
|
||||||
|
|
||||||
if (rangeStart <= joinEnd && joinEnd <= rangeEnd) {
|
if (rangeStart <= joinEnd && joinEnd <= rangeEnd) {
|
||||||
// join range that used to be before the current one, or
|
// join range that used to be before the current one, or
|
||||||
@@ -128,7 +112,7 @@ insert_address_range(addr_range* ranges, uint32* _numRanges, uint32 maxRanges,
|
|||||||
if (numRanges >= maxRanges)
|
if (numRanges >= maxRanges)
|
||||||
return B_ENTRY_NOT_FOUND;
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
ranges[numRanges].start = (addr_t)start;
|
ranges[numRanges].start = (AddressType)start;
|
||||||
ranges[numRanges].size = size;
|
ranges[numRanges].size = size;
|
||||||
(*_numRanges)++;
|
(*_numRanges)++;
|
||||||
|
|
||||||
@@ -136,18 +120,19 @@ insert_address_range(addr_range* ranges, uint32* _numRanges, uint32 maxRanges,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extern "C" status_t
|
template<typename RangeType, typename AddressType, typename SizeType>
|
||||||
remove_address_range(addr_range* ranges, uint32* _numRanges, uint32 maxRanges,
|
static status_t
|
||||||
addr_t start, uint32 size)
|
remove_range(RangeType* ranges, uint32* _numRanges, uint32 maxRanges,
|
||||||
|
AddressType start, SizeType size)
|
||||||
{
|
{
|
||||||
uint32 numRanges = *_numRanges;
|
uint32 numRanges = *_numRanges;
|
||||||
|
|
||||||
addr_t end = ROUNDUP(start + size, B_PAGE_SIZE);
|
AddressType end = ROUNDUP(start + size, B_PAGE_SIZE);
|
||||||
start = ROUNDDOWN(start, B_PAGE_SIZE);
|
start = ROUNDDOWN(start, B_PAGE_SIZE);
|
||||||
|
|
||||||
for (uint32 i = 0; i < numRanges; i++) {
|
for (uint32 i = 0; i < numRanges; i++) {
|
||||||
addr_t rangeStart = ranges[i].start;
|
AddressType rangeStart = ranges[i].start;
|
||||||
addr_t rangeEnd = rangeStart + ranges[i].size;
|
AddressType rangeEnd = rangeStart + ranges[i].size;
|
||||||
|
|
||||||
if (start <= rangeStart) {
|
if (start <= rangeStart) {
|
||||||
if (end <= rangeStart) {
|
if (end <= rangeStart) {
|
||||||
@@ -172,8 +157,8 @@ remove_address_range(addr_range* ranges, uint32* _numRanges, uint32 maxRanges,
|
|||||||
// the range. We keep the head of the range and insert its tail
|
// the range. We keep the head of the range and insert its tail
|
||||||
// as a new range.
|
// as a new range.
|
||||||
ranges[i].size = start - rangeStart;
|
ranges[i].size = start - rangeStart;
|
||||||
return insert_address_range(ranges, _numRanges, maxRanges,
|
return insert_range<RangeType, AddressType, SizeType>(ranges,
|
||||||
end, rangeEnd - end);
|
_numRanges, maxRanges, end, rangeEnd - end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,11 +167,12 @@ remove_address_range(addr_range* ranges, uint32* _numRanges, uint32 maxRanges,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
template<typename RangeType, typename AddressType, typename SizeType>
|
||||||
get_free_address_range(addr_range *ranges, uint32 numRanges, addr_t base,
|
static bool
|
||||||
size_t size, addr_t *_rangeBase)
|
get_free_range(RangeType* ranges, uint32 numRanges, AddressType base,
|
||||||
|
SizeType size, AddressType* _rangeBase)
|
||||||
{
|
{
|
||||||
addr_t end = base + size - 1;
|
AddressType end = base + size - 1;
|
||||||
if (end < base)
|
if (end < base)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -195,8 +181,8 @@ get_free_address_range(addr_range *ranges, uint32 numRanges, addr_t base,
|
|||||||
// intersects with an existing one.
|
// intersects with an existing one.
|
||||||
|
|
||||||
for (uint32 i = 0; i < numRanges;) {
|
for (uint32 i = 0; i < numRanges;) {
|
||||||
addr_t rangeStart = ranges[i].start;
|
AddressType rangeStart = ranges[i].start;
|
||||||
addr_t rangeEnd = ranges[i].start + ranges[i].size - 1;
|
AddressType rangeEnd = ranges[i].start + ranges[i].size - 1;
|
||||||
|
|
||||||
if (base <= rangeEnd && rangeStart <= end) {
|
if (base <= rangeEnd && rangeStart <= end) {
|
||||||
base = rangeEnd + 1;
|
base = rangeEnd + 1;
|
||||||
@@ -216,20 +202,21 @@ get_free_address_range(addr_range *ranges, uint32 numRanges, addr_t base,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
template<typename RangeType, typename AddressType, typename SizeType>
|
||||||
is_address_range_covered(addr_range* ranges, uint32 numRanges, addr_t base,
|
static bool
|
||||||
size_t size)
|
is_range_covered(RangeType* ranges, uint32 numRanges, AddressType base,
|
||||||
|
SizeType size)
|
||||||
{
|
{
|
||||||
// Note: We don't assume that the ranges are sorted, so we can't do this
|
// Note: We don't assume that the ranges are sorted, so we can't do this
|
||||||
// in a simple loop. Instead we restart the loop whenever the start of the
|
// in a simple loop. Instead we restart the loop whenever the start of the
|
||||||
// given range intersects with an existing one.
|
// given range intersects with an existing one.
|
||||||
|
|
||||||
for (uint32 i = 0; i < numRanges;) {
|
for (uint32 i = 0; i < numRanges;) {
|
||||||
addr_t rangeStart = ranges[i].start;
|
AddressType rangeStart = ranges[i].start;
|
||||||
addr_t rangeSize = ranges[i].size;
|
AddressType rangeSize = ranges[i].size;
|
||||||
|
|
||||||
if (rangeStart <= base && rangeSize > base - rangeStart) {
|
if (rangeStart <= base && rangeSize > base - rangeStart) {
|
||||||
size_t intersect = std::min(rangeStart + rangeSize - base, size);
|
SizeType intersect = std::min(rangeStart + rangeSize - base, size);
|
||||||
base += intersect;
|
base += intersect;
|
||||||
size -= intersect;
|
size -= intersect;
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
@@ -246,26 +233,126 @@ is_address_range_covered(addr_range* ranges, uint32 numRanges, addr_t base,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
// #pragma mark -
|
||||||
insert_physical_memory_range(addr_t start, uint32 size)
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
add_kernel_args_range(void* start, size_t size)
|
||||||
{
|
{
|
||||||
return insert_address_range(gKernelArgs.physical_memory_range,
|
return insert_address_range(gKernelArgs.kernel_args_range,
|
||||||
|
&gKernelArgs.num_kernel_args_ranges, MAX_KERNEL_ARGS_RANGE,
|
||||||
|
(addr_t)start, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - addr_range utility functions
|
||||||
|
|
||||||
|
|
||||||
|
/*! Inserts the specified (start, size) pair (aka range) in the
|
||||||
|
addr_range array.
|
||||||
|
It will extend existing ranges in order to have as little
|
||||||
|
ranges in the array as possible.
|
||||||
|
Returns B_OK on success, or B_ENTRY_NOT_FOUND if there was
|
||||||
|
no free array entry available anymore.
|
||||||
|
*/
|
||||||
|
extern "C" status_t
|
||||||
|
insert_address_range(addr_range* ranges, uint32* _numRanges, uint32 maxRanges,
|
||||||
|
addr_t start, size_t size)
|
||||||
|
{
|
||||||
|
return insert_range<addr_range, addr_t, size_t>(ranges, _numRanges,
|
||||||
|
maxRanges, start, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" status_t
|
||||||
|
remove_address_range(addr_range* ranges, uint32* _numRanges, uint32 maxRanges,
|
||||||
|
addr_t start, size_t size)
|
||||||
|
{
|
||||||
|
return remove_range<addr_range, addr_t, size_t>(ranges, _numRanges,
|
||||||
|
maxRanges, start, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
get_free_address_range(addr_range* ranges, uint32 numRanges, addr_t base,
|
||||||
|
size_t size, addr_t* _rangeBase)
|
||||||
|
{
|
||||||
|
return get_free_range<addr_range, addr_t, size_t>(ranges, numRanges, base,
|
||||||
|
size, _rangeBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
is_address_range_covered(addr_range* ranges, uint32 numRanges, addr_t base,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
return is_range_covered<addr_range, addr_t, size_t>(ranges, numRanges, base,
|
||||||
|
size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - phys_addr_range utility functions
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
insert_physical_address_range(phys_addr_range* ranges, uint32* _numRanges,
|
||||||
|
uint32 maxRanges, phys_addr_t start, phys_size_t size)
|
||||||
|
{
|
||||||
|
return insert_range<phys_addr_range, phys_addr_t, phys_size_t>(ranges,
|
||||||
|
_numRanges, maxRanges, start, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
remove_physical_address_range(phys_addr_range* ranges, uint32* _numRanges,
|
||||||
|
uint32 maxRanges, phys_addr_t start, phys_size_t size)
|
||||||
|
{
|
||||||
|
return remove_range<phys_addr_range, phys_addr_t, phys_size_t>(ranges,
|
||||||
|
_numRanges, maxRanges, start, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
get_free_physical_address_range(phys_addr_range* ranges, uint32 numRanges,
|
||||||
|
phys_addr_t base, phys_size_t size, phys_addr_t* _rangeBase)
|
||||||
|
{
|
||||||
|
return get_free_range<phys_addr_range, phys_addr_t, phys_size_t>(ranges,
|
||||||
|
numRanges, base, size, _rangeBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
is_physical_address_range_covered(phys_addr_range* ranges, uint32 numRanges,
|
||||||
|
phys_addr_t base, phys_size_t size)
|
||||||
|
{
|
||||||
|
return is_range_covered<phys_addr_range, phys_addr_t, phys_size_t>(ranges,
|
||||||
|
numRanges, base, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - kernel args range functions
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
insert_physical_memory_range(addr_t start, size_t size)
|
||||||
|
{
|
||||||
|
return insert_physical_address_range(gKernelArgs.physical_memory_range,
|
||||||
&gKernelArgs.num_physical_memory_ranges, MAX_PHYSICAL_MEMORY_RANGE,
|
&gKernelArgs.num_physical_memory_ranges, MAX_PHYSICAL_MEMORY_RANGE,
|
||||||
start, size);
|
start, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
insert_physical_allocated_range(addr_t start, uint32 size)
|
insert_physical_allocated_range(addr_t start, size_t size)
|
||||||
{
|
{
|
||||||
return insert_address_range(gKernelArgs.physical_allocated_range,
|
return insert_physical_address_range(gKernelArgs.physical_allocated_range,
|
||||||
&gKernelArgs.num_physical_allocated_ranges,
|
&gKernelArgs.num_physical_allocated_ranges,
|
||||||
MAX_PHYSICAL_ALLOCATED_RANGE, start, size);
|
MAX_PHYSICAL_ALLOCATED_RANGE, start, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
insert_virtual_allocated_range(addr_t start, uint32 size)
|
insert_virtual_allocated_range(addr_t start, size_t size)
|
||||||
{
|
{
|
||||||
return insert_address_range(gKernelArgs.virtual_allocated_range,
|
return insert_address_range(gKernelArgs.virtual_allocated_range,
|
||||||
&gKernelArgs.num_virtual_allocated_ranges, MAX_VIRTUAL_ALLOCATED_RANGE,
|
&gKernelArgs.num_virtual_allocated_ranges, MAX_VIRTUAL_ALLOCATED_RANGE,
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ static addr_t
|
|||||||
get_next_physical_address(size_t size)
|
get_next_physical_address(size_t size)
|
||||||
{
|
{
|
||||||
addr_t base;
|
addr_t base;
|
||||||
if (!get_free_address_range(gKernelArgs.physical_allocated_range,
|
if (!get_free_physical_address_range(gKernelArgs.physical_allocated_range,
|
||||||
gKernelArgs.num_physical_allocated_ranges, sNextPhysicalAddress,
|
gKernelArgs.num_physical_allocated_ranges, sNextPhysicalAddress,
|
||||||
size, &base)) {
|
size, &base)) {
|
||||||
panic("Out of physical memory!");
|
panic("Out of physical memory!");
|
||||||
@@ -258,10 +258,11 @@ map_page(addr_t virtualAddress, addr_t physicalAddress, uint32 flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename RangeType>
|
||||||
static void
|
static void
|
||||||
sort_addr_range(addr_range *range, int count)
|
sort_addr_range(RangeType *range, int count)
|
||||||
{
|
{
|
||||||
addr_range tempRange;
|
RangeType tempRange;
|
||||||
bool done;
|
bool done;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -270,9 +271,9 @@ sort_addr_range(addr_range *range, int count)
|
|||||||
for (i = 1; i < count; i++) {
|
for (i = 1; i < count; i++) {
|
||||||
if (range[i].start < range[i - 1].start) {
|
if (range[i].start < range[i - 1].start) {
|
||||||
done = false;
|
done = false;
|
||||||
memcpy(&tempRange, &range[i], sizeof(addr_range));
|
memcpy(&tempRange, &range[i], sizeof(RangeType));
|
||||||
memcpy(&range[i], &range[i - 1], sizeof(addr_range));
|
memcpy(&range[i], &range[i - 1], sizeof(RangeType));
|
||||||
memcpy(&range[i - 1], &tempRange, sizeof(addr_range));
|
memcpy(&range[i - 1], &tempRange, sizeof(RangeType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (!done);
|
} while (!done);
|
||||||
@@ -451,14 +452,14 @@ bool
|
|||||||
mmu_allocate_physical(addr_t base, size_t size)
|
mmu_allocate_physical(addr_t base, size_t size)
|
||||||
{
|
{
|
||||||
// check whether the physical memory range exists at all
|
// check whether the physical memory range exists at all
|
||||||
if (!is_address_range_covered(gKernelArgs.physical_memory_range,
|
if (!is_physical_address_range_covered(gKernelArgs.physical_memory_range,
|
||||||
gKernelArgs.num_physical_memory_ranges, base, size)) {
|
gKernelArgs.num_physical_memory_ranges, base, size)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether the physical range is still free
|
// check whether the physical range is still free
|
||||||
addr_t foundBase;
|
addr_t foundBase;
|
||||||
if (!get_free_address_range(gKernelArgs.physical_allocated_range,
|
if (!get_free_physical_address_range(gKernelArgs.physical_allocated_range,
|
||||||
gKernelArgs.num_physical_allocated_ranges, sNextPhysicalAddress,
|
gKernelArgs.num_physical_allocated_ranges, sNextPhysicalAddress,
|
||||||
size, &foundBase) || foundBase != base) {
|
size, &foundBase) || foundBase != base) {
|
||||||
return false;
|
return false;
|
||||||
@@ -717,7 +718,7 @@ mmu_init(void)
|
|||||||
size_t size = gKernelArgs.physical_memory_range[i].size;
|
size_t size = gKernelArgs.physical_memory_range[i].size;
|
||||||
if (size < 64 * 1024) {
|
if (size < 64 * 1024) {
|
||||||
addr_t start = gKernelArgs.physical_memory_range[i].start;
|
addr_t start = gKernelArgs.physical_memory_range[i].start;
|
||||||
remove_address_range(gKernelArgs.physical_memory_range,
|
remove_physical_address_range(gKernelArgs.physical_memory_range,
|
||||||
&gKernelArgs.num_physical_memory_ranges,
|
&gKernelArgs.num_physical_memory_ranges,
|
||||||
MAX_PHYSICAL_MEMORY_RANGE, start, size);
|
MAX_PHYSICAL_MEMORY_RANGE, start, size);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <platform_arch.h>
|
#include <platform_arch.h>
|
||||||
|
#include <boot/addr_range.h>
|
||||||
|
#include <boot/kernel_args.h>
|
||||||
#include <boot/platform.h>
|
#include <boot/platform.h>
|
||||||
#include <boot/stage2.h>
|
#include <boot/stage2.h>
|
||||||
#include <boot/stdio.h>
|
#include <boot/stdio.h>
|
||||||
@@ -38,167 +40,6 @@ extern "C" uint8 __text_begin;
|
|||||||
extern "C" uint8 _end;
|
extern "C" uint8 _end;
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
remove_range_index(addr_range *ranges, uint32 &numRanges, uint32 index)
|
|
||||||
{
|
|
||||||
if (index + 1 == numRanges) {
|
|
||||||
// remove last range
|
|
||||||
numRanges--;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
memmove(&ranges[index], &ranges[index + 1],
|
|
||||||
sizeof(addr_range) * (numRanges - 1 - index));
|
|
||||||
numRanges--;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
|
||||||
insert_memory_range(addr_range *ranges, uint32 &numRanges, uint32 maxRanges,
|
|
||||||
const void *_start, uint32 _size)
|
|
||||||
{
|
|
||||||
addr_t start = ROUNDDOWN(addr_t(_start), B_PAGE_SIZE);
|
|
||||||
addr_t end = ROUNDUP(addr_t(_start) + _size, B_PAGE_SIZE);
|
|
||||||
addr_t size = end - start;
|
|
||||||
if (size == 0)
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
for (uint32 i = 0; i < numRanges; i++) {
|
|
||||||
addr_t rangeStart = ranges[i].start;
|
|
||||||
addr_t rangeEnd = rangeStart + ranges[i].size;
|
|
||||||
|
|
||||||
if (end < rangeStart || start > rangeEnd) {
|
|
||||||
// ranges don't intersect or touch each other
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (start >= rangeStart && end <= rangeEnd) {
|
|
||||||
// range is already completely covered
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start < rangeStart) {
|
|
||||||
// prepend to the existing range
|
|
||||||
ranges[i].start = start;
|
|
||||||
ranges[i].size += rangeStart - start;
|
|
||||||
}
|
|
||||||
if (end > ranges[i].start + ranges[i].size) {
|
|
||||||
// append to the existing range
|
|
||||||
ranges[i].size = end - ranges[i].start;
|
|
||||||
}
|
|
||||||
|
|
||||||
// join ranges if possible
|
|
||||||
|
|
||||||
for (uint32 j = 0; j < numRanges; j++) {
|
|
||||||
if (i == j)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
rangeStart = ranges[i].start;
|
|
||||||
rangeEnd = rangeStart + ranges[i].size;
|
|
||||||
addr_t joinStart = ranges[j].start;
|
|
||||||
addr_t joinEnd = joinStart + ranges[j].size;
|
|
||||||
|
|
||||||
if (rangeStart <= joinEnd && joinEnd <= rangeEnd) {
|
|
||||||
// join range that used to be before the current one, or
|
|
||||||
// the one that's now entirely included by the current one
|
|
||||||
if (joinStart < rangeStart) {
|
|
||||||
ranges[i].size += rangeStart - joinStart;
|
|
||||||
ranges[i].start = joinStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
remove_range_index(ranges, numRanges, j--);
|
|
||||||
} else if (joinStart <= rangeEnd && joinEnd > rangeEnd) {
|
|
||||||
// join range that used to be after the current one
|
|
||||||
ranges[i].size += joinEnd - rangeEnd;
|
|
||||||
|
|
||||||
remove_range_index(ranges, numRanges, j--);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// no range matched, we need to create a new one
|
|
||||||
|
|
||||||
if (numRanges >= maxRanges)
|
|
||||||
return B_ENTRY_NOT_FOUND;
|
|
||||||
|
|
||||||
ranges[numRanges].start = (addr_t)start;
|
|
||||||
ranges[numRanges].size = size;
|
|
||||||
numRanges++;
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
|
||||||
remove_memory_range(addr_range *ranges, uint32 &numRanges, uint32 maxRanges,
|
|
||||||
const void *_start, uint32 _size)
|
|
||||||
{
|
|
||||||
addr_t start = ROUNDDOWN(addr_t(_start), B_PAGE_SIZE);
|
|
||||||
addr_t end = ROUNDUP(addr_t(_start) + _size, B_PAGE_SIZE);
|
|
||||||
|
|
||||||
for (uint32 i = 0; i < numRanges; i++) {
|
|
||||||
addr_t rangeStart = ranges[i].start;
|
|
||||||
addr_t rangeEnd = rangeStart + ranges[i].size;
|
|
||||||
|
|
||||||
if (start <= rangeStart) {
|
|
||||||
if (end <= rangeStart) {
|
|
||||||
// no intersection
|
|
||||||
} else if (end >= rangeEnd) {
|
|
||||||
// remove the complete range
|
|
||||||
remove_range_index(ranges, numRanges, i);
|
|
||||||
i--;
|
|
||||||
} else {
|
|
||||||
// remove the head of the range
|
|
||||||
ranges[i].start = end;
|
|
||||||
ranges[i].size = rangeEnd - end;
|
|
||||||
}
|
|
||||||
} else if (end >= rangeEnd) {
|
|
||||||
if (start < rangeEnd) {
|
|
||||||
// remove the tail
|
|
||||||
ranges[i].size = start - rangeStart;
|
|
||||||
} // else: no intersection
|
|
||||||
} else {
|
|
||||||
// rangeStart < start < end < rangeEnd
|
|
||||||
// The ugly case: We have to remove something from the middle of
|
|
||||||
// the range. We keep the head of the range and insert its tail
|
|
||||||
// as a new range.
|
|
||||||
ranges[i].size = start - rangeStart;
|
|
||||||
return insert_memory_range(ranges, numRanges, maxRanges,
|
|
||||||
(void*)end, rangeEnd - end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
|
||||||
insert_physical_memory_range(void *start, uint32 size)
|
|
||||||
{
|
|
||||||
return insert_memory_range(gKernelArgs.physical_memory_range,
|
|
||||||
gKernelArgs.num_physical_memory_ranges, MAX_PHYSICAL_MEMORY_RANGE,
|
|
||||||
start, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
|
||||||
insert_physical_allocated_range(void *start, uint32 size)
|
|
||||||
{
|
|
||||||
return insert_memory_range(gKernelArgs.physical_allocated_range,
|
|
||||||
gKernelArgs.num_physical_allocated_ranges, MAX_PHYSICAL_ALLOCATED_RANGE,
|
|
||||||
start, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
|
||||||
insert_virtual_allocated_range(void *start, uint32 size)
|
|
||||||
{
|
|
||||||
return insert_memory_range(gKernelArgs.virtual_allocated_range,
|
|
||||||
gKernelArgs.num_virtual_allocated_ranges, MAX_VIRTUAL_ALLOCATED_RANGE,
|
|
||||||
start, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
static status_t
|
static status_t
|
||||||
insert_virtual_range_to_keep(void *start, uint32 size)
|
insert_virtual_range_to_keep(void *start, uint32 size)
|
||||||
@@ -213,9 +54,9 @@ insert_virtual_range_to_keep(void *start, uint32 size)
|
|||||||
static status_t
|
static status_t
|
||||||
remove_virtual_range_to_keep(void *start, uint32 size)
|
remove_virtual_range_to_keep(void *start, uint32 size)
|
||||||
{
|
{
|
||||||
return remove_memory_range(gKernelArgs.arch_args.virtual_ranges_to_keep,
|
return remove_address_range(gKernelArgs.arch_args.virtual_ranges_to_keep,
|
||||||
gKernelArgs.arch_args.num_virtual_ranges_to_keep,
|
&gKernelArgs.arch_args.num_virtual_ranges_to_keep,
|
||||||
MAX_VIRTUAL_RANGES_TO_KEEP, start, size);
|
MAX_VIRTUAL_RANGES_TO_KEEP, (addr_t)start, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -249,8 +90,8 @@ find_physical_memory_ranges(size_t &total)
|
|||||||
|
|
||||||
total += regions[i].size;
|
total += regions[i].size;
|
||||||
|
|
||||||
if (insert_physical_memory_range(regions[i].base, regions[i].size)
|
if (insert_physical_memory_range((addr_t)regions[i].base,
|
||||||
!= B_OK) {
|
regions[i].size) != B_OK) {
|
||||||
printf("cannot map physical memory range (num ranges = %lu)!\n",
|
printf("cannot map physical memory range (num ranges = %lu)!\n",
|
||||||
gKernelArgs.num_physical_memory_ranges);
|
gKernelArgs.num_physical_memory_ranges);
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
@@ -261,77 +102,41 @@ find_physical_memory_ranges(size_t &total)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
|
||||||
is_in_range(addr_range *ranges, uint32 numRanges, void *address, size_t size)
|
|
||||||
{
|
|
||||||
// Note: This function returns whether any single allocated range
|
|
||||||
// completely contains the given range. If the given range crosses
|
|
||||||
// allocated range boundaries, but is nevertheless covered completely, the
|
|
||||||
// function returns false. But since the range management code joins
|
|
||||||
// touching ranges, this should never happen.
|
|
||||||
addr_t start = (addr_t)address;
|
|
||||||
addr_t end = start + size;
|
|
||||||
|
|
||||||
for (uint32 i = 0; i < numRanges; i++) {
|
|
||||||
addr_t rangeStart = ranges[i].start;
|
|
||||||
addr_t rangeEnd = rangeStart + ranges[i].size;
|
|
||||||
|
|
||||||
if ((start >= rangeStart && start < rangeEnd)
|
|
||||||
|| (end >= rangeStart && end < rangeEnd))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool
|
|
||||||
intersects_ranges(addr_range *ranges, uint32 numRanges, void *address,
|
|
||||||
size_t size)
|
|
||||||
{
|
|
||||||
addr_t start = (addr_t)address;
|
|
||||||
addr_t end = start + size;
|
|
||||||
|
|
||||||
for (uint32 i = 0; i < numRanges; i++) {
|
|
||||||
addr_t rangeStart = ranges[i].start;
|
|
||||||
addr_t rangeEnd = rangeStart + ranges[i].size;
|
|
||||||
|
|
||||||
if ((start >= rangeStart && start < rangeEnd)
|
|
||||||
|| (rangeStart >= start && rangeStart < end)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_virtual_allocated(void *address, size_t size)
|
is_virtual_allocated(void *address, size_t size)
|
||||||
{
|
{
|
||||||
return intersects_ranges(gKernelArgs.virtual_allocated_range,
|
addr_t foundBase;
|
||||||
gKernelArgs.num_virtual_allocated_ranges, address, size);
|
return !get_free_address_range(gKernelArgs.virtual_allocated_range,
|
||||||
|
gKernelArgs.num_virtual_allocated_ranges, (addr_t)address, size,
|
||||||
|
&foundBase)
|
||||||
|
|| foundBase != (addr_t)address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_physical_allocated(void *address, size_t size)
|
is_physical_allocated(void *address, size_t size)
|
||||||
{
|
{
|
||||||
return intersects_ranges(gKernelArgs.physical_allocated_range,
|
phys_addr_t foundBase;
|
||||||
gKernelArgs.num_physical_allocated_ranges, address, size);
|
return !get_free_physical_address_range(
|
||||||
|
gKernelArgs.physical_allocated_range,
|
||||||
|
gKernelArgs.num_physical_allocated_ranges, (addr_t)address, size,
|
||||||
|
&foundBase)
|
||||||
|
|| foundBase != (addr_t)address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_physical_memory(void *address, size_t size)
|
is_physical_memory(void *address, size_t size)
|
||||||
{
|
{
|
||||||
return is_in_range(gKernelArgs.physical_memory_range,
|
return is_physical_address_range_covered(gKernelArgs.physical_memory_range,
|
||||||
gKernelArgs.num_physical_memory_ranges, address, size);
|
gKernelArgs.num_physical_memory_ranges, (addr_t)address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_physical_memory(void *address)
|
is_physical_memory(void *address)
|
||||||
{
|
{
|
||||||
return is_physical_memory(address, 0);
|
return is_physical_memory(address, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -453,7 +258,7 @@ find_allocated_ranges(void *oldPageTable, void *pageTable,
|
|||||||
// insert range in physical allocated, if it points to physical memory
|
// insert range in physical allocated, if it points to physical memory
|
||||||
|
|
||||||
if (is_physical_memory(map->physical_address)
|
if (is_physical_memory(map->physical_address)
|
||||||
&& insert_physical_allocated_range(map->physical_address,
|
&& insert_physical_allocated_range((addr_t)map->physical_address,
|
||||||
map->length) != B_OK) {
|
map->length) != B_OK) {
|
||||||
printf("cannot map physical allocated range (num ranges = %lu)!\n",
|
printf("cannot map physical allocated range (num ranges = %lu)!\n",
|
||||||
gKernelArgs.num_physical_allocated_ranges);
|
gKernelArgs.num_physical_allocated_ranges);
|
||||||
@@ -479,7 +284,7 @@ find_allocated_ranges(void *oldPageTable, void *pageTable,
|
|||||||
|
|
||||||
// insert range in virtual allocated
|
// insert range in virtual allocated
|
||||||
|
|
||||||
if (insert_virtual_allocated_range(map->virtual_address,
|
if (insert_virtual_allocated_range((addr_t)map->virtual_address,
|
||||||
map->length) != B_OK) {
|
map->length) != B_OK) {
|
||||||
printf("cannot map virtual allocated range (num ranges = %lu)!\n",
|
printf("cannot map virtual allocated range (num ranges = %lu)!\n",
|
||||||
gKernelArgs.num_virtual_allocated_ranges);
|
gKernelArgs.num_virtual_allocated_ranges);
|
||||||
@@ -656,8 +461,8 @@ arch_mmu_allocate(void *_virtualAddress, size_t size, uint8 _protection,
|
|||||||
|
|
||||||
printf("mmu_alloc: va %p, pa %p, size %u\n", virtualAddress,
|
printf("mmu_alloc: va %p, pa %p, size %u\n", virtualAddress,
|
||||||
physicalAddress, size);
|
physicalAddress, size);
|
||||||
insert_virtual_allocated_range(virtualAddress, size);
|
insert_virtual_allocated_range((addr_t)virtualAddress, size);
|
||||||
insert_physical_allocated_range(physicalAddress, size);
|
insert_physical_allocated_range((addr_t)physicalAddress, size);
|
||||||
|
|
||||||
map_range(virtualAddress, physicalAddress, size, protection);
|
map_range(virtualAddress, physicalAddress, size, protection);
|
||||||
|
|
||||||
@@ -706,14 +511,16 @@ map_callback(struct of_arguments *args)
|
|||||||
// insert range in physical allocated if needed
|
// insert range in physical allocated if needed
|
||||||
|
|
||||||
if (is_physical_memory(physicalAddress)
|
if (is_physical_memory(physicalAddress)
|
||||||
&& insert_physical_allocated_range(physicalAddress, length) != B_OK) {
|
&& insert_physical_allocated_range((addr_t)physicalAddress, length)
|
||||||
|
!= B_OK) {
|
||||||
error = -1;
|
error = -1;
|
||||||
return OF_FAILED;
|
return OF_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert range in virtual allocated
|
// insert range in virtual allocated
|
||||||
|
|
||||||
if (insert_virtual_allocated_range(virtualAddress, length) != B_OK) {
|
if (insert_virtual_allocated_range((addr_t)virtualAddress, length)
|
||||||
|
!= B_OK) {
|
||||||
error = -2;
|
error = -2;
|
||||||
return OF_FAILED;
|
return OF_FAILED;
|
||||||
}
|
}
|
||||||
@@ -976,16 +783,16 @@ arch_mmu_init(void)
|
|||||||
//map_range((void *)realBase, (void *)realBase, realSize * 2, PAGE_READ_WRITE);
|
//map_range((void *)realBase, (void *)realBase, realSize * 2, PAGE_READ_WRITE);
|
||||||
//map_range((void *)(total - realSize), (void *)(total - realSize), realSize, PAGE_READ_WRITE);
|
//map_range((void *)(total - realSize), (void *)(total - realSize), realSize, PAGE_READ_WRITE);
|
||||||
//map_range((void *)table, (void *)table, tableSize, PAGE_READ_WRITE);
|
//map_range((void *)table, (void *)table, tableSize, PAGE_READ_WRITE);
|
||||||
insert_physical_allocated_range((void *)realBase, realSize * 2);
|
insert_physical_allocated_range(realBase, realSize * 2);
|
||||||
insert_virtual_allocated_range((void *)realBase, realSize * 2);
|
insert_virtual_allocated_range(realBase, realSize * 2);
|
||||||
insert_physical_allocated_range((void *)(total - realSize), realSize);
|
insert_physical_allocated_range(total - realSize, realSize);
|
||||||
insert_virtual_allocated_range((void *)(total - realSize), realSize);
|
insert_virtual_allocated_range(total - realSize, realSize);
|
||||||
insert_physical_allocated_range((void *)table, tableSize);
|
insert_physical_allocated_range((addr_t)table, tableSize);
|
||||||
insert_virtual_allocated_range((void *)table, tableSize);
|
insert_virtual_allocated_range((addr_t)table, tableSize);
|
||||||
|
|
||||||
// QEMU OpenHackware work-around
|
// QEMU OpenHackware work-around
|
||||||
insert_physical_allocated_range((void *)0x05800000, 0x06000000 - 0x05800000);
|
insert_physical_allocated_range(0x05800000, 0x06000000 - 0x05800000);
|
||||||
insert_virtual_allocated_range((void *)0x05800000, 0x06000000 - 0x05800000);
|
insert_virtual_allocated_range(0x05800000, 0x06000000 - 0x05800000);
|
||||||
|
|
||||||
physicalTable = table;
|
physicalTable = table;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user