1195331Simp#
2195331Simp# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3195331Simp#
4195331Simp# SPDX-License-Identifier: GPL-2.0-only
5195331Simp#
6195331Simp
7195331Simp''' generate a header file for the elfloader from a device tree '''
8195331Simp
9195331Simpimport argparse
10195331Simpimport builtins
11195331Simpimport logging
12195331Simpimport pyfdt.pyfdt
13195331Simp
14195331Simpfrom jinja2 import Environment, BaseLoader
15195331Simpfrom typing import List
16195331Simp
17195331Simpfrom hardware import config, device, fdt
18195331Simpfrom hardware.utils import cpu, memory, rule
19195331Simp
20195331Simp
21195331SimpHEADER_TEMPLATE = '''/*
22195331Simp * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
23195331Simp *
24195331Simp * SPDX-License-Identifier: GPL-2.0-only
25195331Simp */
26195331Simp
27195331Simp/*
28211158Sneel * This file is autogenerated by kernel/tools/hardware_gen.py
29195331Simp */
30195331Simp
31195331Simp#pragma once
32195331Simp
33195331Simp#include <types.h>
34195331Simp{% if uses_psci %}
35195331Simp#include <psci.h>
36195331Simp{% endif %}
37195331Simp
38195331Simp#define MAX_NUM_REGIONS {{ max_reg }}
39195331Simp
40195331Simpstruct elfloader_driver;
41195331Simp
42195331Simpstruct elfloader_device {
43195331Simp    const char *compat;
44195331Simp    volatile void *region_bases[MAX_NUM_REGIONS];
45211158Sneel    struct elfloader_driver *drv;
46211158Sneel};
47195331Simp
48211158Sneelstruct elfloader_cpu {
49195331Simp    const char *compat;
50195331Simp    const char *enable_method;
51195331Simp    word_t cpu_id;
52195331Simp    word_t extra_data;
53195331Simp};
54195331Simp
55211158Sneel#ifdef DRIVER_COMMON
56195331Simpstruct elfloader_device elfloader_devices[] = {
57195331Simp{% for d in devices %}
58211158Sneel    {
59195331Simp        /* {{ d['path'] }} */
60195331Simp        .compat = "{{ d['compat'] }}",
61195331Simp        .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