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