123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- # 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()
|