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