# # Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) # # SPDX-License-Identifier: BSD-2-Clause # ''' Wrapper around a dict of pages for some extra functionality. Only intended to be used internally. ''' from __future__ import absolute_import, division, print_function, \ unicode_literals from .Cap import Cap from .Object import ASIDPool, Frame from .Spec import Spec from .util import round_down, PAGE_SIZE, lookup_architecture import collections def consume(iterator): '''Take a generator and exhaust it. Useful for discarding the unused result of something that would otherwise accumulate in memory. Clagged from https://docs.python.org/2/library/itertools.html''' # feed the entire iterator into a zero-length deque collections.deque(iterator, maxlen=0) class PageCollection(object): def __init__(self, name='', arch='arm11', infer_asid=True, vspace_root=None): self.name = name self.arch = arch self._pages = {} self._vspace_root = vspace_root self._asid = None self.infer_asid = infer_asid self._spec = None def add_page(self, vaddr, read=False, write=False, execute=False, size=PAGE_SIZE, elffill=[]): if vaddr not in self._pages: # Only create this page if we don't already have it. self._pages[vaddr] = { 'read': False, 'write': False, 'execute': False, 'size': PAGE_SIZE, 'elffill': [], } # Now upgrade this page's permissions to meet our current requirements. self._pages[vaddr]['read'] |= read self._pages[vaddr]['write'] |= write self._pages[vaddr]['execute'] |= execute self._pages[vaddr]['size'] = size self._pages[vaddr]['elffill'].extend(elffill) def __getitem__(self, key): return self._pages[key] def __iter__(self): return self._pages.__iter__() def get_vspace_root(self): if not self._vspace_root: vspace = lookup_architecture(self.arch).vspace() self._vspace_root = vspace.make_object('%s_%s' % (vspace.type_name, self.name)) return self._vspace_root, Cap(self._vspace_root) def get_asid(self): if not self._asid and self.infer_asid: self._asid = ASIDPool('asid_%s' % self.name) self._asid[0] = self.get_vspace_root()[1] return self._asid def _get_page_cap(self, existing_frames, page, page_vaddr, page_counter, spec): ''' Get a mapping cap from somewhere. First check if the existing_frames we were given contain a cap already. Otherwise create a Frame and Cap from the mapping information we have. ''' if page_vaddr in existing_frames: (size, cap) = existing_frames[page_vaddr] assert size == page['size'] return cap frame = Frame('frame_%s_%04d' % (self.name, page_counter), page['size']) spec.add_object(frame) return Cap(frame, read=page['read'], write=page['write'], grant=page['execute']) def get_spec(self, existing_frames={}): if self._spec is not None: return self._spec spec = Spec(self.arch) # Page directory and ASID. vspace_root, vspace_root_cap = self.get_vspace_root() spec.add_object(vspace_root) asid = self.get_asid() if asid is not None: spec.add_object(asid) # Construct frames and infer page objects from the pages. vspace = spec.arch.vspace() object_counter = 0 objects = {} for page_counter, (page_vaddr, page) in enumerate(self._pages.items()): page_cap = self._get_page_cap(existing_frames, page, page_vaddr, page_counter, spec) # Walk the hierarchy, creating missing objects until we can # insert the frame level = vspace parent = vspace_root while level.child is not None and page['size'] < level.child.coverage: level = level.child object_vaddr = level.base_vaddr(page_vaddr) object_index = level.parent_index(object_vaddr) if (level, object_vaddr) not in objects: object = level.make_object('%s_%s_%04d' % ( level.type_name, self.name, object_counter)) object_counter += 1 spec.add_object(object) object_cap = Cap(object) parent[object_index] = object_cap objects[(level, object_vaddr)] = object parent = parent[object_index].referent object_counter += 1 if page_cap and page_cap.mapping_deferred: # This cap requires set_mapping to be called on it to provide a reference # to the mapping container and index. This is so the loader can use the same # cap for mapping and then copy it into the target cspace. Otherwise the cap # would be copied and therefore be an unmapped cap. page_cap.set_mapping(parent, level.child_index(page_vaddr)) page_cap = Cap(page_cap.referent, read=page_cap.read, write=page_cap.write, grant=page_cap.grant, cached=page_cap.cached) parent[level.child_index(page_vaddr)] = page_cap if page_cap: page["elffill"].extend(page_cap.referent.fill) page_cap.referent.fill = page["elffill"] # Cache the result for next time. assert self._spec is None self._spec = spec return spec def create_address_space(regions, name='', arch='arm11'): assert isinstance(regions, list) pages = PageCollection(name, arch) for r in regions: assert 'start' in r assert 'end' in r v = round_down(r['start']) while round_down(v) < r['end']: pages.add_page(v, r.get('read', False), r.get('write', False), r.get('execute', False)) v += PAGE_SIZE return pages