| #!/usr/bin/env python |
| # |
| # Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) |
| # |
| # SPDX-License-Identifier: BSD-2-Clause |
| # |
| |
| from capdl.Object import register_object_sizes, Untyped |
| from capdl.Allocator import ASIDTableAllocator, BestFitAllocator |
| from capdl import ELF, lookup_architecture, TCB, valid_architectures |
| from importlib.metadata import version |
| from jinja2 import Environment, BaseLoader, FileSystemLoader |
| import sys |
| import argparse |
| import pickle |
| import logging |
| import os |
| import tempfile |
| import yaml |
| import six |
| |
| |
| CSPACE_TEMPLATE_FILE = os.path.join(os.path.dirname(__file__), "templates/cspace.template.c") |
| |
| |
| def manifest(cap_symbols, region_symbols, architecture, targets): |
| """ |
| Generates a c file from CSPACE_TEMPLATE_FILE with some runtime information |
| about CSpace slots and special address ranges |
| """ |
| temp_file = open(CSPACE_TEMPLATE_FILE, 'r').read() |
| template = Environment(loader=BaseLoader).from_string(temp_file) |
| |
| for (e, ccspace) in targets: |
| name = os.path.basename(e) |
| if ccspace: |
| data = template.render( |
| {'slots': cap_symbols[name], 'symbols': region_symbols[name], 'progname': name, 'ipc_buffer_symbol': "mainIpcBuffer"}) |
| ccspace.write(data) |
| |
| |
| def final_spec(args, obj_space, cspaces, addr_spaces, targets, architecture): |
| """ |
| Generates a final CapDL spec file that can be given to a capdl loader application |
| """ |
| arch = lookup_architecture(architecture) |
| |
| for e, key in targets: |
| name = os.path.basename(e) |
| elf = ELF(e, name, architecture) |
| cspace = cspaces[key] |
| |
| # Avoid inferring a TCB as we've already created our own. |
| elf_spec = elf.get_spec(infer_tcb=False, infer_asid=False, |
| pd=addr_spaces[key].vspace_root, addr_space=addr_spaces[key]) |
| obj_space.merge(elf_spec, label=key) |
| for slot, v in cspace.cnode.slots.items(): |
| if v is not None and isinstance(v.referent, TCB): |
| tcb = v.referent |
| if 'cspace' in tcb and tcb['cspace'] and tcb['cspace'].referent is not cspace.cnode: |
| # We exclude TCBs that refer to a different CSpace |
| continue |
| funcs = {"get_vaddr": lambda x: elf.get_symbol_vaddr(x)} |
| tcb.ip = eval(str(tcb.ip), {"__builtins__": None}, funcs) |
| tcb.sp = eval(str(tcb.sp), {"__builtins__": None}, funcs) |
| tcb.addr = eval(str(tcb.addr), {"__builtins__": None}, funcs) |
| tcb.init = eval(str(tcb.init), {"__builtins__": None}, funcs) |
| if not args.fprovide_tcb_caps: |
| del cspace.cnode[slot] |
| cspace.cnode.finalise_size(arch=arch) |
| return obj_space |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser( |
| description="") |
| parser.add_argument('--architecture', '--arch', default='aarch32', |
| type=lambda x: type('')(x).lower(), choices=valid_architectures(), |
| help='Target architecture.') |
| parser.add_argument('--object-sizes', required=True, type=argparse.FileType('r')) |
| subparsers = parser.add_subparsers() |
| parser_a = subparsers.add_parser('build_cnode') |
| parser_a.add_argument('--ccspace', nargs='+', type=argparse.FileType('w'), action='append') |
| parser_a.set_defaults(which="build_cnode") |
| parser_a.add_argument('--manifest-in', type=argparse.FileType('rb')) |
| parser_a.add_argument('--elffile', nargs='+', action='append') |
| parser_b = subparsers.add_parser('gen_cdl') |
| parser_b.add_argument('--outfile', type=argparse.FileType('w')) |
| parser_b.set_defaults(which="gen_cdl") |
| parser_b.add_argument('--manifest-in', type=argparse.FileType('rb')) |
| parser_b.add_argument('--elffile', nargs='+', action='append') |
| parser_b.add_argument('--keys', nargs='+', action='append') |
| parser_b.add_argument('--fprovide-tcb-caps', action='store_true', |
| default=True, help='Hand out TCB caps to components, allowing them to ' |
| 'exit cleanly.') |
| parser_b.add_argument('--fno-provide-tcb-caps', action='store_false', |
| dest='fprovide_tcb_caps', help='Do not hand out TCB caps, causing ' |
| 'components to fault on exiting.') |
| parser_b.add_argument('--save-object-state', type=argparse.FileType('wb')) |
| parser_b.add_argument('--static-alloc', action='store_true', |
| help='Perform static object allocation (requires --untyped)') |
| parser_b.add_argument('--dynamic-alloc', action='store_false', dest='static_alloc', |
| help='Cancel --static-alloc') |
| parser_b.add_argument('--untyped', type=argparse.FileType('r'), |
| help="YAML file with available seL4 bootinfo untypeds") |
| |
| args = parser.parse_args() |
| register_object_sizes(yaml.load(args.object_sizes, Loader=yaml.FullLoader)) |
| |
| if args.which == "build_cnode": |
| data = yaml.load(args.manifest_in, Loader=yaml.FullLoader) |
| assert 'cap_symbols' in data and 'region_symbols' in data, "Invalid file format" |
| elfs = [item for sublist in args.elffile for item in sublist] |
| cspaces = [item for sublist in args.ccspace for item in sublist] |
| targets = zip(elfs, cspaces) |
| manifest(data['cap_symbols'], data['region_symbols'], args.architecture, targets) |
| return 0 |
| |
| if args.which == "gen_cdl": |
| if args.static_alloc and not args.untyped: |
| parser.error('--static-alloc requires --untyped') |
| |
| allocator_state = pickle.load(args.manifest_in) |
| elfs = [item for sublist in args.elffile for item in sublist] |
| keys = [item for sublist in args.keys for item in sublist] |
| targets = zip(elfs, keys) |
| obj_space = final_spec(args, allocator_state.obj_space, allocator_state.cspaces, |
| allocator_state.addr_spaces, targets, args.architecture) |
| |
| # Calculate final layout for objects and ASID slots... |
| ASIDTableAllocator().allocate(obj_space.spec) |
| if args.static_alloc: |
| alloc = BestFitAllocator() |
| for ut in yaml.load(args.untyped): |
| if len(ut): |
| is_device, paddr, size_bits = ut['device'], ut['paddr'], ut['size_bits'] |
| alloc.add_untyped(Untyped("root_untyped_0x%x" % paddr, |
| size_bits=size_bits, paddr=paddr), is_device) |
| alloc.allocate(obj_space.spec) |
| |
| args.outfile.write(repr(obj_space.spec)) |
| if args.save_object_state: |
| pickle.dump(allocator_state, args.save_object_state) |
| |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| jinja2_version = version("jinja2") |
| if jinja2_version < "2.10": |
| sys.exit("Jinja2 should be >= 2.10") |
| |
| sys.exit(main()) |