1# 2# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 3# 4# SPDX-License-Identifier: GPL-2.0-only 5# 6 7from typing import List, Set 8 9import hardware.utils as utils 10 11from hardware.config import Config 12from hardware.device import WrappedNode 13from hardware.fdt import FdtParser 14from hardware.memory import Region 15from hardware.utils.rule import KernelRegionGroup 16 17 18def get_memory_regions(tree: FdtParser): 19 ''' Get all regions with device_type = memory in the tree ''' 20 regions = set() 21 22 def visitor(node: WrappedNode): 23 if node.has_prop('device_type') and node.get_prop('device_type').strings[0] == 'memory': 24 regions.update(node.get_regions()) 25 tree.visit(visitor) 26 return regions 27 28 29def parse_reserved_regions(node: WrappedNode) -> Set[Region]: 30 ''' Parse a reserved-memory node, looking for regions that are 31 unusable by OS (e.g. reserved for firmware/bootloader) ''' 32 if node is None: 33 return set() 34 35 ret = set() 36 for child in node: 37 if child.has_prop('reg') and child.has_prop('no-map'): 38 ret.update(child.get_regions()) 39 return ret 40 41 42def reserve_regions(regions: Set[Region], reserved: Set[Region]) -> Set[Region]: 43 ''' Given a set of regions, and a set of reserved regions, 44 return a new set that is the first set of regions minus the second set. ''' 45 ret = set(regions) 46 47 while len(reserved) > 0: 48 reserve = reserved.pop() 49 new_ret = set() 50 for el in ret: 51 r = el.reserve(reserve) 52 new_ret.update(r) 53 ret = new_ret 54 return ret 55 56 57def align_memory(regions: Set[Region], config: Config) -> List[Region]: 58 ''' Given a set of regions, sort them and align the first so that the ELF loader will be able to load the kernel into it. Will return the aligned 59 memory region list, a set of any regions of memory that were aligned out 60 and the physBase value that the kernel will use. ''' 61 ret = sorted(regions) 62 extra_reserved = set() 63 64 if config.arch == 'riscv': 65 # RISC-V is special: it expects physBase to be 66 # the address that the bootloader is loaded at. 67 physBase = ret[0].base 68 69 if config.get_bootloader_reserve() > 0: 70 resv = Region(ret[0].base, config.get_bootloader_reserve(), None) 71 extra_reserved.add(resv) 72 ret[0].base += config.get_bootloader_reserve() 73 ret[0].size -= config.get_bootloader_reserve() 74 75 if config.get_kernel_phys_align() != 0: 76 new = ret[0].align_base(config.get_kernel_phys_align()) 77 resv = Region(ret[0].base, new.base - ret[0].base, None) 78 extra_reserved.add(resv) 79 ret[0] = new 80 81 if config.arch != 'riscv': 82 # ARM (and presumably other architectures) 83 # want physBase to be the physical load address of the kernel. 84 physBase = ret[0].base 85 return ret, extra_reserved, physBase 86 87 88def get_physical_memory(tree: FdtParser, config: Config) -> List[Region]: 89 ''' returns a list of regions representing physical memory as used by the kernel ''' 90 regions = get_memory_regions(tree) 91 reserved = parse_reserved_regions(tree.get_path('/reserved-memory')) 92 regions = reserve_regions(regions, reserved) 93 regions, extra_reserved, physBase = align_memory(regions, config) 94 95 return regions, reserved.union(extra_reserved), physBase 96 97 98def get_addrspace_exclude(regions: List[Region], config: Config): 99 ''' Returns a list of regions that represents the inverse of the given region list. ''' 100 ret = set() 101 as_max = utils.align_down(config.addrspace_max, config.get_page_bits()) 102 ret.add(Region(0, as_max, None)) 103 104 for reg in regions: 105 if type(reg) == KernelRegionGroup: 106 if reg.user_ok: 107 continue 108 new_ret = set() 109 for el in ret: 110 new_ret.update(el.reserve(reg)) 111 112 ret = new_ret 113 return sorted(ret, key=lambda a: a.base) 114