mirror of
https://github.com/google/pebble.git
synced 2025-03-15 16:51:21 +00:00
102 lines
3.7 KiB
Python
102 lines
3.7 KiB
Python
|
# Copyright 2024 Google LLC
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
|
||
|
from collections import namedtuple
|
||
|
from bitarray import bitarray
|
||
|
import struct
|
||
|
|
||
|
MIN_REGION_SIZE = 32
|
||
|
MAX_REGION_SIZE = 4 * 1024 * 1024 * 1024 # 4 GB
|
||
|
NUM_SUBREGIONS = 8
|
||
|
|
||
|
|
||
|
def round_up_to_power_of_two(x):
|
||
|
""" Find the next power of two that is eqaul to or greater than x
|
||
|
|
||
|
>>> round_up_to_power_of_two(4)
|
||
|
4
|
||
|
>>> round_up_to_power_of_two(5)
|
||
|
8
|
||
|
"""
|
||
|
|
||
|
return 2**((x-1).bit_length())
|
||
|
|
||
|
|
||
|
MpuRegion = namedtuple('MpuRegion', ['address', 'size', 'disabled_subregion'])
|
||
|
|
||
|
|
||
|
def find_subregions_for_region(address, size):
|
||
|
""" Find a MPU region configuration that will exactly match the provided combination of
|
||
|
address and size.
|
||
|
|
||
|
This is non trivial, as the MPU only supports regions that are power of 2 sized and
|
||
|
are aligned to their size. To make this slightly more flexible, the region is then
|
||
|
split into 8 subregions that are equally sized and can be individually enabled and
|
||
|
disabled.
|
||
|
|
||
|
>>> find_subregions_for_region(0x0, 512)
|
||
|
MpuRegion(address=0, size=512, disabled_subregion=0)
|
||
|
>>> find_subregions_for_region(0x0, 513)
|
||
|
Traceback (most recent call last):
|
||
|
...
|
||
|
Exception: No solution found
|
||
|
|
||
|
For example, the snowy layout
|
||
|
Result is a 256kb region at 0x20000000 with a region disabled mask of 0b11000111
|
||
|
>>> find_subregions_for_region(0x20018000, 96 * 1024)
|
||
|
MpuRegion(address=536870912, size=262144, disabled_subregion=199)
|
||
|
"""
|
||
|
|
||
|
# Find the range of sizes to attempt to match against. Anything smaller than the size of the
|
||
|
# region itself wont work, and anything where a single subregion is too big won't work either.
|
||
|
smallest_block_size = max(round_up_to_power_of_two(size), MIN_REGION_SIZE)
|
||
|
largest_block_size = min(round_up_to_power_of_two(size * NUM_SUBREGIONS), MAX_REGION_SIZE)
|
||
|
|
||
|
# Iterate over the potentional candidates from smallest to largest
|
||
|
current_block_size = smallest_block_size
|
||
|
while current_block_size <= largest_block_size:
|
||
|
subregion_size = current_block_size // NUM_SUBREGIONS
|
||
|
|
||
|
start_in_block = address % current_block_size
|
||
|
end_in_block = start_in_block + size
|
||
|
|
||
|
if (start_in_block % subregion_size == 0 and
|
||
|
end_in_block % subregion_size == 0 and
|
||
|
end_in_block <= current_block_size):
|
||
|
|
||
|
# This region fits in the provided region and both the start and end are aligned with
|
||
|
# subregion boundries. This will work!
|
||
|
|
||
|
block_start_addresss = address - start_in_block
|
||
|
|
||
|
start_enabled_subregion = start_in_block / subregion_size
|
||
|
end_enabled_subregion = end_in_block / subregion_size
|
||
|
|
||
|
disabled_subregions = bitarray(8, endian='little')
|
||
|
disabled_subregions.setall(True)
|
||
|
disabled_subregions[start_enabled_subregion:end_enabled_subregion] = False
|
||
|
|
||
|
disabled_subregions_bytes = disabled_subregions.tobytes()
|
||
|
disabled_subregions_int, = struct.unpack('B', disabled_subregions_bytes)
|
||
|
|
||
|
return MpuRegion(block_start_addresss, current_block_size, disabled_subregions_int)
|
||
|
|
||
|
current_block_size *= 2
|
||
|
else:
|
||
|
raise Exception("No solution found")
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
import doctest
|
||
|
doctest.testmod()
|