mpu_calc.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. # Copyright 2024 Google LLC
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from collections import namedtuple
  15. from bitarray import bitarray
  16. import struct
  17. MIN_REGION_SIZE = 32
  18. MAX_REGION_SIZE = 4 * 1024 * 1024 * 1024 # 4 GB
  19. NUM_SUBREGIONS = 8
  20. def round_up_to_power_of_two(x):
  21. """ Find the next power of two that is eqaul to or greater than x
  22. >>> round_up_to_power_of_two(4)
  23. 4
  24. >>> round_up_to_power_of_two(5)
  25. 8
  26. """
  27. return 2**((x-1).bit_length())
  28. MpuRegion = namedtuple('MpuRegion', ['address', 'size', 'disabled_subregion'])
  29. def find_subregions_for_region(address, size):
  30. """ Find a MPU region configuration that will exactly match the provided combination of
  31. address and size.
  32. This is non trivial, as the MPU only supports regions that are power of 2 sized and
  33. are aligned to their size. To make this slightly more flexible, the region is then
  34. split into 8 subregions that are equally sized and can be individually enabled and
  35. disabled.
  36. >>> find_subregions_for_region(0x0, 512)
  37. MpuRegion(address=0, size=512, disabled_subregion=0)
  38. >>> find_subregions_for_region(0x0, 513)
  39. Traceback (most recent call last):
  40. ...
  41. Exception: No solution found
  42. For example, the snowy layout
  43. Result is a 256kb region at 0x20000000 with a region disabled mask of 0b11000111
  44. >>> find_subregions_for_region(0x20018000, 96 * 1024)
  45. MpuRegion(address=536870912, size=262144, disabled_subregion=199)
  46. """
  47. # Find the range of sizes to attempt to match against. Anything smaller than the size of the
  48. # region itself wont work, and anything where a single subregion is too big won't work either.
  49. smallest_block_size = max(round_up_to_power_of_two(size), MIN_REGION_SIZE)
  50. largest_block_size = min(round_up_to_power_of_two(size * NUM_SUBREGIONS), MAX_REGION_SIZE)
  51. # Iterate over the potentional candidates from smallest to largest
  52. current_block_size = smallest_block_size
  53. while current_block_size <= largest_block_size:
  54. subregion_size = current_block_size // NUM_SUBREGIONS
  55. start_in_block = address % current_block_size
  56. end_in_block = start_in_block + size
  57. if (start_in_block % subregion_size == 0 and
  58. end_in_block % subregion_size == 0 and
  59. end_in_block <= current_block_size):
  60. # This region fits in the provided region and both the start and end are aligned with
  61. # subregion boundries. This will work!
  62. block_start_addresss = address - start_in_block
  63. start_enabled_subregion = start_in_block // subregion_size
  64. end_enabled_subregion = end_in_block // subregion_size
  65. disabled_subregions = bitarray(8, endian='little')
  66. disabled_subregions.setall(True)
  67. disabled_subregions[start_enabled_subregion:end_enabled_subregion] = False
  68. disabled_subregions_bytes = disabled_subregions.tobytes()
  69. disabled_subregions_int, = struct.unpack('B', disabled_subregions_bytes)
  70. return MpuRegion(block_start_addresss, current_block_size, disabled_subregions_int)
  71. current_block_size *= 2
  72. else:
  73. raise Exception("No solution found")
  74. if __name__ == '__main__':
  75. import doctest
  76. doctest.testmod()