1# 2# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 3# 4# SPDX-License-Identifier: BSD-2-Clause 5# 6 7''' 8Wrapper around a dict of pages for some extra functionality. Only intended to 9be used internally. 10''' 11 12from __future__ import absolute_import, division, print_function, \ 13 unicode_literals 14 15from .Cap import Cap 16from .Object import ASIDPool, Frame 17from .Spec import Spec 18from .util import round_down, PAGE_SIZE, lookup_architecture 19import collections 20 21 22def consume(iterator): 23 '''Take a generator and exhaust it. Useful for discarding the unused result 24 of something that would otherwise accumulate in memory. Clagged from 25 https://docs.python.org/2/library/itertools.html''' 26 # feed the entire iterator into a zero-length deque 27 collections.deque(iterator, maxlen=0) 28 29 30class PageCollection(object): 31 def __init__(self, name='', arch='arm11', infer_asid=True, vspace_root=None): 32 self.name = name 33 self.arch = arch 34 self._pages = {} 35 self._vspace_root = vspace_root 36 self._asid = None 37 self.infer_asid = infer_asid 38 self._spec = None 39 40 def add_page(self, vaddr, read=False, write=False, execute=False, size=PAGE_SIZE, elffill=[]): 41 if vaddr not in self._pages: 42 # Only create this page if we don't already have it. 43 self._pages[vaddr] = { 44 'read': False, 45 'write': False, 46 'execute': False, 47 'size': PAGE_SIZE, 48 'elffill': [], 49 } 50 # Now upgrade this page's permissions to meet our current requirements. 51 self._pages[vaddr]['read'] |= read 52 self._pages[vaddr]['write'] |= write 53 self._pages[vaddr]['execute'] |= execute 54 self._pages[vaddr]['size'] = size 55 self._pages[vaddr]['elffill'].extend(elffill) 56 57 def __getitem__(self, key): 58 return self._pages[key] 59 60 def __iter__(self): 61 return self._pages.__iter__() 62 63 def get_vspace_root(self): 64 if not self._vspace_root: 65 vspace = lookup_architecture(self.arch).vspace() 66 self._vspace_root = vspace.make_object('%s_%s' % (vspace.type_name, self.name)) 67 return self._vspace_root, Cap(self._vspace_root) 68 69 def get_asid(self): 70 if not self._asid and self.infer_asid: 71 self._asid = ASIDPool('asid_%s' % self.name) 72 self._asid[0] = self.get_vspace_root()[1] 73 return self._asid 74 75 def _get_page_cap(self, existing_frames, page, page_vaddr, page_counter, spec): 76 ''' 77 Get a mapping cap from somewhere. First check if the existing_frames we 78 were given contain a cap already. Otherwise create a Frame and Cap from 79 the mapping information we have. 80 ''' 81 if page_vaddr in existing_frames: 82 (size, cap) = existing_frames[page_vaddr] 83 assert size == page['size'] 84 return cap 85 frame = Frame('frame_%s_%04d' % (self.name, page_counter), 86 page['size']) 87 spec.add_object(frame) 88 return Cap(frame, read=page['read'], write=page['write'], 89 grant=page['execute']) 90 91 def get_spec(self, existing_frames={}): 92 if self._spec is not None: 93 return self._spec 94 95 spec = Spec(self.arch) 96 97 # Page directory and ASID. 98 vspace_root, vspace_root_cap = self.get_vspace_root() 99 spec.add_object(vspace_root) 100 asid = self.get_asid() 101 if asid is not None: 102 spec.add_object(asid) 103 104 # Construct frames and infer page objects from the pages. 105 vspace = spec.arch.vspace() 106 object_counter = 0 107 objects = {} 108 for page_counter, (page_vaddr, page) in enumerate(self._pages.items()): 109 page_cap = self._get_page_cap(existing_frames, page, page_vaddr, page_counter, spec) 110 # Walk the hierarchy, creating missing objects until we can 111 # insert the frame 112 level = vspace 113 parent = vspace_root 114 while level.child is not None and page['size'] < level.child.coverage: 115 level = level.child 116 object_vaddr = level.base_vaddr(page_vaddr) 117 object_index = level.parent_index(object_vaddr) 118 if (level, object_vaddr) not in objects: 119 object = level.make_object('%s_%s_%04d' % ( 120 level.type_name, self.name, object_counter)) 121 object_counter += 1 122 spec.add_object(object) 123 object_cap = Cap(object) 124 parent[object_index] = object_cap 125 objects[(level, object_vaddr)] = object 126 parent = parent[object_index].referent 127 object_counter += 1 128 if page_cap and page_cap.mapping_deferred: 129 # This cap requires set_mapping to be called on it to provide a reference 130 # to the mapping container and index. This is so the loader can use the same 131 # cap for mapping and then copy it into the target cspace. Otherwise the cap 132 # would be copied and therefore be an unmapped cap. 133 page_cap.set_mapping(parent, level.child_index(page_vaddr)) 134 page_cap = Cap(page_cap.referent, read=page_cap.read, 135 write=page_cap.write, grant=page_cap.grant, cached=page_cap.cached) 136 parent[level.child_index(page_vaddr)] = page_cap 137 if page_cap: 138 page["elffill"].extend(page_cap.referent.fill) 139 page_cap.referent.fill = page["elffill"] 140 141 # Cache the result for next time. 142 assert self._spec is None 143 self._spec = spec 144 145 return spec 146 147 148def create_address_space(regions, name='', arch='arm11'): 149 assert isinstance(regions, list) 150 151 pages = PageCollection(name, arch) 152 for r in regions: 153 assert 'start' in r 154 assert 'end' in r 155 v = round_down(r['start']) 156 while round_down(v) < r['end']: 157 pages.add_page(v, r.get('read', False), r.get('write', False), 158 r.get('execute', False)) 159 v += PAGE_SIZE 160 161 return pages 162