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