1#
2# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6
7''' generate a c header file from the device tree '''
8import argparse
9import builtins
10
11from jinja2 import Environment, BaseLoader
12
13from hardware import config, fdt
14from hardware.utils import memory, rule
15
16HEADER_TEMPLATE = '''/*
17 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
18 *
19 * SPDX-License-Identifier: GPL-2.0-only
20 */
21
22/*
23 * This file is autogenerated by kernel/tools/hardware_gen.py.
24 */
25
26#ifndef __PLAT_DEVICES_GEN_H
27#define __PLAT_DEVICES_GEN_H
28#include <linker.h>
29
30#ifndef KDEV_BASE
31#include <mode/hardware.h>
32#endif
33
34#define physBase {{ "0x{:x}".format(physBase) }}
35
36/* INTERRUPTS */
37{% for irq in kernel_irqs %}
38/* {{ irq.desc }} */
39{% if irq.has_enable() %}
40{{ irq.get_enable_macro_str() }}
41{% endif %}
42{% if irq.has_sel() %}
43{{ irq.get_sel_macro_str() }}
44{% endif %}
45#define {{ irq.label }} {{ irq.irq }}
46{% if irq.has_sel() %}
47#else
48#define {{ irq.label }} {{ irq.false_irq }}
49{{ irq.get_sel_endif() }}
50{% endif %}
51{% if irq.has_enable() %}
52{{ irq.get_enable_endif() }}
53{% endif %}
54
55{% endfor -%}
56
57/* KERNEL DEVICES */
58{% for (addr, macro) in sorted(kernel_macros.items()) %}
59#define {{ macro }} (KDEV_BASE + {{ "0x{:x}".format(addr) }})
60{% endfor %}
61
62#ifndef __ASSEMBLER__
63{% if len(kernel_regions) > 0 %}
64static const kernel_frame_t BOOT_RODATA kernel_devices[] = {
65    {% for group in kernel_regions %}
66    {% if group.has_macro() %}
67    {{ group.get_macro() }}
68    {% endif %}
69    /* {{ group.get_desc() }} */
70    {% for reg in group.regions %}
71    {
72        {% set map_addr = group.get_map_offset(reg) %}
73        {{ "0x{:x}".format(reg.base) }},
74        {% if map_addr in kernel_macros %}
75        {{ kernel_macros[map_addr] }},
76        {% else %}
77        /* contains {{ ', '.join(group.labels.keys()) }} */
78        KDEV_BASE + {{ "0x{:x}".format(map_addr) }},
79        {% endif %}
80        {% if args.arch == 'arm' %}
81        true, /* armExecuteNever */
82        {% endif %}
83        {% if group.user_ok %}
84        true, /* userAvailable */
85        {% else %}
86        false, /* userAvailable */
87        {% endif %}
88    },
89    {% endfor %}
90    {% if group.has_macro() %}
91    {{ group.get_endif() }}
92    {% endif %}
93    {% endfor %}
94};
95{% else %}
96static const kernel_frame_t BOOT_RODATA *const kernel_devices = NULL;
97{% endif %}
98
99/* PHYSICAL MEMORY */
100static const p_region_t BOOT_RODATA avail_p_regs[] = {
101    {% for reg in physical_memory %}
102    { {{ "0x{:x}".format(reg.base) }}, {{ "0x{:x}".format(reg.base + reg.size) }} }, /* {{reg.owner.path}} */
103    {% endfor %}
104};
105
106#endif /* !__ASSEMBLER__ */
107
108#endif /* __PLAT_DEVICES_GEN_H */
109'''
110
111
112def get_kernel_devices(tree: fdt.FdtParser, rules: rule.HardwareYaml):
113    ''' Given a device tree and a set of rules, returns a tuple (groups, offsets).
114
115        Groups is a list of 'KernelRegionGroups', each of which represents a single contiguous region of memory that is associated with a device.
116        Offsets is a dict of offset -> label, where label is the name given to the kernel for that address (e.g. SERIAL_PPTR) and offset is the offset from KDEV_BASE at which it's mapped.'''
117    kernel_devices = tree.get_kernel_devices()
118
119    kernel_offset = 0
120    groups = []
121    for dev in kernel_devices:
122        dev_rule = rules.get_rule(dev)
123        new_regions = dev_rule.get_regions(dev)
124        for reg in new_regions:
125            if reg in groups:
126                other = groups[groups.index(reg)]
127                other.take_labels(reg)
128            else:
129                groups.append(reg)
130
131    offsets = {}
132    for group in groups:
133        kernel_offset = group.set_kernel_offset(kernel_offset)
134        offsets.update(group.get_labelled_addresses())
135    return (groups, offsets)
136
137
138def get_interrupts(tree: fdt.FdtParser, rules: rule.HardwareYaml):
139    ''' Get dict of interrupts, {label: KernelInterrupt} from the DT and hardware rules. '''
140    kernel_devices = tree.get_kernel_devices()
141
142    irqs = []
143    for dev in kernel_devices:
144        dev_rule = rules.get_rule(dev)
145        print(f"interrupts for device {dev.path}")
146        irqs += dev_rule.get_interrupts(tree, dev)
147
148    ret = {}
149    for irq in irqs:
150        if irq.label in ret:
151            if irq.prio > ret[irq.label].prio:
152                ret[irq.label] = irq
153        else:
154            ret[irq.label] = irq
155
156    ret = list(ret.values())
157    ret.sort(key=lambda a: a.label)
158    return ret
159
160
161def run(tree: fdt.FdtParser, hardware: rule.HardwareYaml, config: config.Config, args: argparse.Namespace):
162    if not args.header_out:
163        raise ValueError('You need to specify a header-out to use c header output')
164
165    physical_memory, reserved, physBase = memory.get_physical_memory(tree, config)
166    kernel_regions, kernel_macros = get_kernel_devices(tree, hardware)
167    kernel_irqs = get_interrupts(tree, hardware)
168    template = Environment(loader=BaseLoader, trim_blocks=True,
169                           lstrip_blocks=True).from_string(HEADER_TEMPLATE)
170
171    template_args = dict(builtins.__dict__, **{
172        'args': args,
173        'kernel_irqs': kernel_irqs,
174        'kernel_macros': kernel_macros,
175        'kernel_regions': kernel_regions,
176        'physBase': physBase,
177        'physical_memory': physical_memory,
178    })
179
180    data = template.render(template_args)
181    args.header_out.write(data)
182    args.header_out.close()
183
184
185def add_args(parser):
186    parser.add_argument('--header-out', help='output file for c header',
187                        type=argparse.FileType('w'))
188