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