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 header file for the elfloader from a device tree '''
8
9import argparse
10import builtins
11import logging
12import pyfdt.pyfdt
13
14from jinja2 import Environment, BaseLoader
15from typing import List
16
17from hardware import config, device, fdt
18from hardware.utils import cpu, memory, rule
19
20
21HEADER_TEMPLATE = '''/*
22 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
23 *
24 * SPDX-License-Identifier: GPL-2.0-only
25 */
26
27/*
28 * This file is autogenerated by kernel/tools/hardware_gen.py
29 */
30
31#pragma once
32
33#include <types.h>
34{% if uses_psci %}
35#include <psci.h>
36{% endif %}
37
38#define MAX_NUM_REGIONS {{ max_reg }}
39
40struct elfloader_driver;
41
42struct elfloader_device {
43    const char *compat;
44    volatile void *region_bases[MAX_NUM_REGIONS];
45    struct elfloader_driver *drv;
46};
47
48struct elfloader_cpu {
49    const char *compat;
50    const char *enable_method;
51    word_t cpu_id;
52    word_t extra_data;
53};
54
55#ifdef DRIVER_COMMON
56struct elfloader_device elfloader_devices[] = {
57{% for d in devices %}
58    {
59        /* {{ d['path'] }} */
60        .compat = "{{ d['compat'] }}",
61        .region_bases = {
62            {% for r in d['regions'] %}
63            (void *){{ "0x{:x}".format(r.base) }},
64            {% endfor %}
65            {% for i in range(max_reg - len(d['regions'])) %}
66            (void *)0,
67            {% endfor %}
68        },
69    },
70{% endfor %}
71{% if devices | length == 0 %}
72    {
73        .compat = NULL,
74        .region_bases = {
75            {% for i in range(max_reg) %}
76            NULL,
77            {% endfor %}
78        },
79    },
80{% endif %}
81};
82
83struct elfloader_cpu elfloader_cpus[] = {
84    {% for cpu in cpus %}
85    {
86        /* {{ cpu['path'] }} */
87        .compat = "{{ cpu['compat'] }}",
88        .enable_method = {{ '"{}"'.format(cpu['enable_method']) if cpu['enable_method'] else 'NULL' }},
89        .cpu_id = {{ "0x{:x}".format(cpu['cpuid']) }},
90        .extra_data = {{ cpu['extra'] }}
91    },
92    {% endfor %}
93    { .compat = NULL /* sentinel */ },
94};
95#else
96extern struct elfloader_device elfloader_devices[];
97extern struct elfloader_cpu elfloader_cpus[];
98#endif
99'''
100
101
102def get_elfloader_cpus(tree: fdt.FdtParser, devices: List[device.WrappedNode]) -> List[dict]:
103    cpus = cpu.get_cpus(tree)
104    PSCI_COMPAT = ['arm,psci-0.2', 'arm,psci-1.0']
105    psci_node = [n for n in devices if n.has_prop('compatible')
106                 and n.get_prop('compatible').strings[0] in PSCI_COMPAT]
107
108    if len(psci_node) > 0:
109        psci_node = psci_node[0]
110    else:
111        psci_node = None
112
113    cpu_info = []
114    for i, cpu_node in enumerate(sorted(cpus, key=lambda a: a.path)):
115        enable_method = None
116        if cpu_node.has_prop('enable-method'):
117            enable_method = cpu_node.get_prop('enable-method').strings[0]
118
119        cpuid = i
120        if cpu_node.has_prop('reg'):
121            cpuid = cpu_node.parse_address(list(cpu_node.get_prop('reg').words))
122
123        extra_data = 0
124        if enable_method == 'psci' and psci_node:
125            extra_data = 'PSCI_METHOD_' + psci_node.get_prop('method').strings[0].upper()
126        elif enable_method == 'spin-table':
127            extra_data = '0x{:x}'.format(
128                device.Utils.make_number(2, list(cpu_node.get_prop('cpu-release-addr').words)))
129
130        obj = {
131            'compat': cpu_node.get_prop('compatible').strings[0],
132            'enable_method': enable_method,
133            'cpuid': cpuid,
134            'path': cpu_node.path,
135            'extra': extra_data,
136        }
137        cpu_info.append(obj)
138
139    # guarantee that cpus in the same cluster will be consecutive
140    return sorted(cpu_info, key=lambda a: a['cpuid'])
141
142
143def run(tree: fdt.FdtParser, hardware: rule.HardwareYaml, config: config.Config, args: argparse.Namespace):
144    devices = tree.get_elfloader_devices()
145    cpu_info = get_elfloader_cpus(tree, devices)
146
147    max_reg = 1
148    device_info = []
149    for dev in devices:
150        obj = {
151            'compat': hardware.get_matched_compatible(dev),
152            'path': dev.path,
153            'regions': dev.get_regions()
154        }
155        max_reg = max(len(obj['regions']), max_reg)
156
157        device_info.append(obj)
158
159    device_info.sort(key=lambda a: a['compat'])
160
161    template = Environment(loader=BaseLoader, trim_blocks=True,
162                           lstrip_blocks=True).from_string(HEADER_TEMPLATE)
163
164    template_args = dict(builtins.__dict__, **{
165        'cpus': cpu_info,
166        'devices': device_info,
167        'max_reg': max_reg,
168        'uses_psci': any([c['enable_method'] == 'psci' for c in cpu_info])
169    })
170
171    data = template.render(template_args)
172    args.elfloader_out.write(data)
173    args.elfloader_out.close()
174
175
176def add_args(parser):
177    parser.add_argument('--elfloader-out', help='output file for elfloader header',
178                        type=argparse.FileType('w'))
179