1#!/usr/bin/env python3
2#
3# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
4#
5# SPDX-License-Identifier: GPL-2.0-only
6#
7
8from __future__ import print_function, division
9import argparse
10import logging
11import yaml
12
13from hardware import config, fdt
14from hardware.outputs import c_header, compat_strings, yaml as yaml_out, elfloader
15from hardware.utils.rule import HardwareYaml
16
17OUTPUTS = {
18    'c_header': c_header,
19    'compat_strings': compat_strings,
20    'elfloader': elfloader,
21    'yaml': yaml_out,
22}
23
24
25def validate_rules(rules, schema):
26    ''' Try and validate a hardware rules file against a schema.
27        If jsonschema is not installed, succeed with a warning. '''
28    try:
29        from jsonschema import validate
30        return validate(rules, schema)
31    except ImportError:
32        logging.warning('Skipping hardware YAML validation; `pip install jsonschema` to validate')
33        return True
34
35
36def add_task_args(outputs: dict, parser: argparse.ArgumentParser):
37    ''' Add arguments for each output type. '''
38    for t in sorted(outputs.keys()):
39        task = outputs[t]
40        name = t.replace('_', '-')
41        group = parser.add_argument_group('{} pass'.format(name))
42        group.add_argument('--' + name, help=task.__doc__.strip(), action='store_true')
43        task.add_args(group)
44
45
46def main(args: argparse.Namespace):
47    ''' Parse the DT and hardware config YAML and run each
48    selected output method. '''
49    cfg = config.get_arch_config(args.arch, args.addrspace_max)
50    parsed_dt = fdt.FdtParser(args.dtb)
51    rules = yaml.load(args.hardware_config, Loader=yaml.FullLoader)
52    schema = yaml.load(args.hardware_schema, Loader=yaml.FullLoader)
53    validate_rules(rules, schema)
54    hardware = HardwareYaml(rules, cfg)
55
56    arg_dict = vars(args)
57    for t in sorted(OUTPUTS.keys()):
58        if arg_dict[t]:
59            OUTPUTS[t].run(parsed_dt, hardware, cfg, args)
60
61
62if __name__ == '__main__':
63    parser = argparse.ArgumentParser(
64        description='transform device tree input to seL4 build configuration artefacts'
65    )
66
67    parser.add_argument('--dtb', help='device tree blob to parse for generation',
68                        required=True, type=argparse.FileType('rb'))
69    parser.add_argument('--hardware-config', help='YAML file containing configuration for kernel devices',
70                        required=True, type=argparse.FileType('r'))
71    parser.add_argument('--hardware-schema', help='YAML file containing schema for hardware config',
72                        required=True, type=argparse.FileType('r'))
73    parser.add_argument('--arch', help='architecture to generate for', default='arm')
74    parser.add_argument('--addrspace-max',
75                        help='maximum address that is available as device untyped', type=int, default=32)
76
77    parser.add_argument('--enable-profiling', help='enable profiling',
78                        action='store_const', const=True, default=False)
79
80    add_task_args(OUTPUTS, parser)
81
82    args = parser.parse_args()
83
84    if args.enable_profiling:
85        import cProfile
86        cProfile.run('main(args)', sort='cumtime')
87    else:
88        main(args)
89