blob: 2be2fc5acbf4e0c95910c4f7bb5fad8088fca547 [file] [log] [blame]
#
# 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