1#!/usr/bin/env python3
2#
3# © 2021 Qualcomm Innovation Center, Inc. All rights reserved.
4#
5# SPDX-License-Identifier: BSD-3-Clause
6
7import os
8from Cheetah.Template import Template
9from Cheetah import ImportHooks
10
11
12def xreg_range(*args):
13    return tuple('x{:d}'.format(r) for r in range(*args))
14
15
16templates_dir = os.path.join('tools', 'hypercalls', 'templates')
17abis = {}
18
19
20class abi():
21    def __init__(self, hypcall_base):
22        self.hypcall_base = hypcall_base
23
24
25class abi_aarch64(abi):
26    def __init__(self, hypcall_base):
27        super().__init__(hypcall_base)
28
29        # The size in bytes of each machine register
30        self.register_size = 8
31
32        # Registers used for parameters and results. Note that we don't
33        # support indirect results (i.e. structs larger than 16 bytes).
34        self.parameter_reg = xreg_range(0, 8)
35        self.result_reg = xreg_range(0, 8)
36
37        # Registers clobbered by the hypervisor.
38        self.caller_saved_reg = xreg_range(8, 18)
39
40    @classmethod
41    def register_name(cls, size, index):
42        reg_type = "x" if size == 8 else "w"
43        return "{}{}".format(reg_type, index)
44
45
46# HVC 0 is used for ARM SMCCC (PSCI, etc). Gunyah uses 0x6XXX
47abis['aarch64'] = abi_aarch64(0x6000)
48
49# Dictionary with all hypercalls defined
50hypcall_dict = dict()
51vendor_hypcall_list = []
52
53
54class Variable:
55    def __init__(self, ctype, name, type_definition):
56        self.ctype = ctype
57        self.name = name
58        self.size = type_definition.size
59        self.category = type_definition.category
60        if type_definition.category == "bitfield":
61            self.type_name = type_definition.type_name
62        if type_definition.category == "union":
63            try:
64                raw_type, _ = type_definition.named_member('raw')
65                assert raw_type.size == self.size
66            except Exception:
67                raise Exception(
68                    "Public unions must have a correctly sized 'raw' member")
69        self.ignore = name.startswith('res0') or name.startswith(
70            'res1') or name.endswith('_')
71        self.pointer = False
72        try:
73            from ir import PointerType
74            d = type_definition
75            if isinstance(d.compound_type, PointerType):
76                self.pointer = True
77        except AttributeError:
78            pass
79        if self.ignore:
80            if name.startswith('res0'):
81                self.default = 0
82            elif name.startswith('res1'):
83                self.default = 0xffffffffffffffff
84            elif name.endswith('_'):
85                raise Exception(
86                    "Invalid name ending with underscore: {:s}".format(name))
87            else:
88                raise Exception("Invalid ignored name {:s}".format(name))
89
90
91class Hypercall:
92    def __init__(self, name, num, properties, abi):
93        self.name = name
94        self.num = num
95        self.used_regs = set()
96        self.inputs = []
97        self.input_count = 0
98        self.outputs = []
99        self.output_count = 0
100        self.clobbers = set()
101        self.abi = abis[abi]
102        self.properties = properties
103
104        self.hvc_num = "0x{:x}".format(self.abi.hypcall_base + num)
105
106    def check_type(self, var, role):
107        if var.size > self.abi.register_size:
108            raise Exception('Hypercall {:s}: {:s} {:s} has type {:s}, which '
109                            'does not fit in a {:d}-byte machine register '
110                            '(size is {:d} bytes)'.format(
111                                self.name, role, var.name, var.ctype,
112                                self.abi.register_size, var.size))
113
114    def add_input(self, input):
115        self.check_type(input, 'input')
116        reg = self.abi.parameter_reg[self.input_count]
117        self.used_regs.add(reg)
118        self.inputs.append((reg, input))
119        self.input_count += 1
120
121    def add_output(self, output):
122        self.check_type(output, 'output')
123        reg = self.abi.result_reg[self.output_count]
124        self.used_regs.add(reg)
125        self.outputs.append((reg, output))
126        self.output_count += 1
127
128    def finalise(self):
129        if 'vendor_hyp_call' in self.properties:
130            vendor_hypcall_list.append(self)
131        else:
132            hypcall_dict[self.num] = self
133
134        self.inputs = tuple(self.inputs)
135        self.outputs = tuple(self.outputs)
136
137        # Calculate register clobber list for guest interface
138        self.clobbers.update((x for x in self.abi.parameter_reg
139                              if x not in self.used_regs))
140        self.clobbers.update((x for x in self.abi.result_reg
141                              if x not in self.used_regs))
142        self.clobbers.update(self.abi.caller_saved_reg)
143
144
145ns = locals()
146
147
148def apply_template(template_file):
149    ImportHooks.install()
150    template = Template(file=template_file, searchList=ns)
151    result = str(template)
152    ImportHooks.uninstall()
153    return result
154