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